Skip to content

Commit 564ab12

Browse files
committed
feat(gen): copy types
1 parent e33e40f commit 564ab12

File tree

8 files changed

+396
-5
lines changed

8 files changed

+396
-5
lines changed

lib/gen/go.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,18 @@ export function generateGo (schema, options = {}) {
265265
typesrc += `func (${typeName}_${memberType}) ${typeName.charAt(0).toLowerCase() + typeName.slice(1)}() {}\n\n`
266266
}
267267
}
268+
} else if ('copy' in typeDefn) {
269+
const { fromType } = typeDefn.copy
270+
// Resolve fromType - it could be a primitive or custom type
271+
let resolvedType = fromType
272+
273+
// Check if it's a primitive IPLD type
274+
if (['Bool', 'Int', 'Float', 'String', 'Bytes', 'Link'].includes(fromType)) {
275+
resolvedType = getGoType([], fromType)
276+
resolvedType = fixGoType(imports, resolvedType, false)
277+
}
278+
279+
typesrc += `type ${typeName} = ${resolvedType}\n\n`
268280
}
269281
}
270282

lib/gen/rust.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ export function generateRust (schema) {
357357

358358
typesrc += '}\n\n'
359359
}
360+
} else if ('copy' in typeDefn) {
361+
const { fromType } = typeDefn.copy
362+
// Resolve fromType - it could be a primitive or custom type
363+
let resolvedType = fromType
364+
365+
// Check if it's a primitive IPLD type
366+
if (['Bool', 'Int', 'Float', 'String', 'Bytes', 'Link'].includes(fromType)) {
367+
resolvedType = getRustType([], fromType)
368+
resolvedType = fixRustType(imports, resolvedType, false)
369+
}
370+
371+
typesrc += `pub type ${typeName} = ${resolvedType};\n\n`
360372
}
361373
}
362374

lib/gen/typescript.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,21 @@ export function generateTypeScript (schema) {
273273
}
274274
} else if ('copy' in typeDefn) {
275275
const { fromType } = typeDefn.copy
276-
typesrc += `export type ${typeName} = ${fromType}\n\n`
276+
277+
// Resolve fromType - it could be a primitive or custom type
278+
let resolvedType = fromType
279+
let validatorFunc = `${fromType}.is${fromType}(value)`
280+
281+
// Check if it's a primitive IPLD type
282+
if (['Bool', 'Int', 'Float', 'String', 'Bytes', 'Link'].includes(fromType)) {
283+
resolvedType = fixTypeScriptType(imports, `@ipld/schema/schema-schema.js#Kind${fromType}`, false)
284+
validatorFunc = `${resolvedType}.is${resolvedType}(value)`
285+
}
286+
287+
typesrc += `export type ${typeName} = ${resolvedType}\n\n`
277288
typesrc += `export namespace ${typeName} {\n`
278289
typesrc += ` export function is${typeName}(value: any): value is ${typeName} {\n`
279-
typesrc += ` return ${fromType}.is${fromType}(value)\n`
290+
typesrc += ` return ${validatorFunc}\n`
280291
typesrc += ' }\n'
281292
typesrc += '}\n\n'
282293
} else if (['bool', 'string', 'bytes', 'int', 'float', 'link', 'null'].includes(typeKind)) {

test/fixtures/gen/copy-types.md

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
# Copy Types (Type Aliases)
2+
3+
Testing copy type generation for Go, Rust, and TypeScript.
4+
5+
[testmark]:# (test/schema)
6+
```ipldsch
7+
# Basic copy types
8+
type UserID = String
9+
type Age = Int
10+
type Balance = Float
11+
type Data = Bytes
12+
type Reference = Link
13+
14+
# Copy of struct
15+
type Person struct {
16+
name String
17+
age Int
18+
}
19+
20+
type Employee = Person
21+
22+
# Copy of enum
23+
type Status enum {
24+
| Active
25+
| Inactive
26+
| Pending
27+
} representation string
28+
29+
type UserStatus = Status
30+
31+
# Copy of list
32+
type Names [String]
33+
type TeamMembers = Names
34+
35+
# Copy of map
36+
type Settings {String: String}
37+
type Configuration = Settings
38+
39+
# Chained copies
40+
type ID = String
41+
type UserIdentifier = ID
42+
type AdminID = UserIdentifier
43+
44+
# Copy with annotations (annotations should not carry over)
45+
# @rusttype(custom::Type)
46+
type SpecialInt = Int
47+
48+
type RegularInt = SpecialInt
49+
```
50+
51+
[testmark]:# (test/golang)
52+
```go
53+
package main
54+
55+
import (
56+
"github.com/ipfs/go-cid"
57+
)
58+
59+
type UserID = string
60+
61+
type Age = int64
62+
63+
type Balance = float64
64+
65+
type Data = []byte
66+
67+
type Reference = cid.Cid
68+
69+
type Person struct {
70+
name string
71+
age int64
72+
}
73+
74+
type Employee = Person
75+
76+
type Status string
77+
78+
const (
79+
StatusActive Status = "Active"
80+
StatusInactive Status = "Inactive"
81+
StatusPending Status = "Pending"
82+
)
83+
84+
type UserStatus = Status
85+
86+
type Names []string
87+
88+
type TeamMembers = Names
89+
90+
type Settings map[string]string
91+
92+
type Configuration = Settings
93+
94+
type ID = string
95+
96+
type UserIdentifier = ID
97+
98+
type AdminID = UserIdentifier
99+
100+
type SpecialInt = int64
101+
102+
type RegularInt = SpecialInt
103+
```
104+
105+
[testmark]:# (test/rust)
106+
```rust
107+
use cid::Cid;
108+
use serde::{Deserialize, Serialize};
109+
use std::collections::HashMap;
110+
111+
pub type UserID = String;
112+
113+
pub type Age = i64;
114+
115+
pub type Balance = f64;
116+
117+
pub type Data = Vec<u8>;
118+
119+
pub type Reference = Cid;
120+
121+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
122+
pub struct Person {
123+
pub name: String,
124+
pub age: i64,
125+
}
126+
127+
pub type Employee = Person;
128+
129+
#[derive(Deserialize, Serialize)]
130+
pub enum Status {
131+
Active,
132+
Inactive,
133+
Pending,
134+
}
135+
136+
pub type UserStatus = Status;
137+
138+
pub type Names = Vec<String>;
139+
140+
pub type TeamMembers = Names;
141+
142+
pub type Settings = HashMap<String, String>;
143+
144+
pub type Configuration = Settings;
145+
146+
pub type ID = String;
147+
148+
pub type UserIdentifier = ID;
149+
150+
pub type AdminID = UserIdentifier;
151+
152+
pub type SpecialInt = i64;
153+
154+
pub type RegularInt = SpecialInt;
155+
```
156+
157+
[testmark]:# (test/typescript)
158+
```typescript
159+
import {
160+
KindBytes,
161+
KindFloat,
162+
KindInt,
163+
KindLink,
164+
KindMap,
165+
KindString,
166+
} from '@ipld/schema/schema-schema.js'
167+
168+
export type UserID = KindString
169+
170+
export namespace UserID {
171+
export function isUserID(value: any): value is UserID {
172+
return KindString.isKindString(value)
173+
}
174+
}
175+
176+
export type Age = KindInt
177+
178+
export namespace Age {
179+
export function isAge(value: any): value is Age {
180+
return KindInt.isKindInt(value)
181+
}
182+
}
183+
184+
export type Balance = KindFloat
185+
186+
export namespace Balance {
187+
export function isBalance(value: any): value is Balance {
188+
return KindFloat.isKindFloat(value)
189+
}
190+
}
191+
192+
export type Data = KindBytes
193+
194+
export namespace Data {
195+
export function isData(value: any): value is Data {
196+
return KindBytes.isKindBytes(value)
197+
}
198+
}
199+
200+
export type Reference = KindLink
201+
202+
export namespace Reference {
203+
export function isReference(value: any): value is Reference {
204+
return KindLink.isKindLink(value)
205+
}
206+
}
207+
208+
export type Person = {
209+
name: KindString
210+
age: KindInt
211+
}
212+
213+
export namespace Person {
214+
export function isPerson(value: any): value is Person {
215+
if (!KindMap.isKindMap(value)) {
216+
return false
217+
}
218+
const keyCount = Object.keys(value).length
219+
return keyCount === 2 &&
220+
('name' in value && (KindString.isKindString(value.name))) &&
221+
('age' in value && (KindInt.isKindInt(value.age)))
222+
}
223+
}
224+
225+
export type Employee = Person
226+
227+
export namespace Employee {
228+
export function isEmployee(value: any): value is Employee {
229+
return Person.isPerson(value)
230+
}
231+
}
232+
233+
export type Status = "Active" | "Inactive" | "Pending"
234+
235+
export namespace Status {
236+
export const Active: Status = "Active"
237+
export const Inactive: Status = "Inactive"
238+
export const Pending: Status = "Pending"
239+
240+
export function isStatus(value: any): value is Status {
241+
return value === "Active" || value === "Inactive" || value === "Pending"
242+
}
243+
}
244+
245+
export type UserStatus = Status
246+
247+
export namespace UserStatus {
248+
export function isUserStatus(value: any): value is UserStatus {
249+
return Status.isStatus(value)
250+
}
251+
}
252+
253+
export type Names = KindString[]
254+
255+
export namespace Names {
256+
export function isNames(value: any): value is Names {
257+
return Array.isArray(value) && value.every(KindString.isKindString)
258+
}
259+
}
260+
261+
export type TeamMembers = Names
262+
263+
export namespace TeamMembers {
264+
export function isTeamMembers(value: any): value is TeamMembers {
265+
return Names.isNames(value)
266+
}
267+
}
268+
269+
export type Settings = { [key: string]: KindString }
270+
271+
export namespace Settings {
272+
export function isSettings(value: any): value is Settings {
273+
if (!KindMap.isKindMap(value)) {
274+
return false
275+
}
276+
return Object.values(value).every(v => KindString.isKindString(v))
277+
}
278+
}
279+
280+
export type Configuration = Settings
281+
282+
export namespace Configuration {
283+
export function isConfiguration(value: any): value is Configuration {
284+
return Settings.isSettings(value)
285+
}
286+
}
287+
288+
export type ID = KindString
289+
290+
export namespace ID {
291+
export function isID(value: any): value is ID {
292+
return KindString.isKindString(value)
293+
}
294+
}
295+
296+
export type UserIdentifier = ID
297+
298+
export namespace UserIdentifier {
299+
export function isUserIdentifier(value: any): value is UserIdentifier {
300+
return ID.isID(value)
301+
}
302+
}
303+
304+
export type AdminID = UserIdentifier
305+
306+
export namespace AdminID {
307+
export function isAdminID(value: any): value is AdminID {
308+
return UserIdentifier.isUserIdentifier(value)
309+
}
310+
}
311+
312+
export type SpecialInt = KindInt
313+
314+
export namespace SpecialInt {
315+
export function isSpecialInt(value: any): value is SpecialInt {
316+
return KindInt.isKindInt(value)
317+
}
318+
}
319+
320+
export type RegularInt = SpecialInt
321+
322+
export namespace RegularInt {
323+
export function isRegularInt(value: any): value is RegularInt {
324+
return SpecialInt.isSpecialInt(value)
325+
}
326+
}
327+
```

0 commit comments

Comments
 (0)