Skip to content

Commit 669361d

Browse files
committed
Add optional json annotations for custom types
1 parent e571f05 commit 669361d

File tree

4 files changed

+96
-27
lines changed

4 files changed

+96
-27
lines changed

packages/go-gen/src/go.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ impl Display for GoField {
5050
self.ty,
5151
self.rust_name
5252
)?;
53+
if let Some(annotations) = &self.ty.json_annotations {
54+
write!(f, ",{}", annotations)?;
55+
}
5356
if self.ty.is_nullable {
5457
f.write_str(",omitempty")?;
5558
}
@@ -64,6 +67,8 @@ pub struct GoType {
6467
/// This will add `omitempty` to the json tag and use a pointer type if
6568
/// the type is not a basic type
6669
pub is_nullable: bool,
70+
/// Additional json annotations, if any
71+
pub json_annotations: Option<String>,
6772
}
6873

6974
impl GoType {
@@ -123,22 +128,26 @@ mod tests {
123128
let ty = GoType {
124129
name: "string".to_string(),
125130
is_nullable: true,
131+
json_annotations: None,
126132
};
127133
let ty2 = GoType {
128134
name: "string".to_string(),
129135
is_nullable: false,
136+
json_annotations: None,
130137
};
131138
assert_eq!(format!("{}", ty), "string");
132139
assert_eq!(format!("{}", ty2), "string");
133140

134141
let ty = GoType {
135142
name: "FooBar".to_string(),
136143
is_nullable: true,
144+
json_annotations: None,
137145
};
138146
assert_eq!(format!("{}", ty), "*FooBar");
139147
let ty = GoType {
140148
name: "FooBar".to_string(),
141149
is_nullable: false,
150+
json_annotations: None,
142151
};
143152
assert_eq!(format!("{}", ty), "FooBar");
144153
}
@@ -151,6 +160,7 @@ mod tests {
151160
ty: GoType {
152161
name: "string".to_string(),
153162
is_nullable: true,
163+
json_annotations: None,
154164
},
155165
};
156166
assert_eq!(
@@ -164,6 +174,7 @@ mod tests {
164174
ty: GoType {
165175
name: "string".to_string(),
166176
is_nullable: false,
177+
json_annotations: None,
167178
},
168179
};
169180
assert_eq!(format!("{}", field), "FooBar string `json:\"foo_bar\"`");
@@ -174,6 +185,7 @@ mod tests {
174185
ty: GoType {
175186
name: "FooBar".to_string(),
176187
is_nullable: true,
188+
json_annotations: None,
177189
},
178190
};
179191
assert_eq!(
@@ -190,12 +202,28 @@ mod tests {
190202
ty: GoType {
191203
name: "string".to_string(),
192204
is_nullable: true,
205+
json_annotations: None,
193206
},
194207
};
195208
assert_eq!(
196209
format!("{}", field),
197210
"// foo_bar is a test field\nFooBar string `json:\"foo_bar,omitempty\"`"
198211
);
212+
213+
// now with additional json annotations
214+
let field = GoField {
215+
rust_name: "foo_bar".to_string(),
216+
docs: None,
217+
ty: GoType {
218+
name: "uint64".to_string(),
219+
is_nullable: true,
220+
json_annotations: Some("string".to_string()),
221+
},
222+
};
223+
assert_eq!(
224+
format!("{}", field),
225+
"FooBar uint64 `json:\"foo_bar,string,omitempty\"`"
226+
);
199227
}
200228

201229
#[test]
@@ -209,6 +237,7 @@ mod tests {
209237
ty: GoType {
210238
name: "string".to_string(),
211239
is_nullable: true,
240+
json_annotations: None,
212241
},
213242
}],
214243
};
@@ -226,6 +255,7 @@ mod tests {
226255
ty: GoType {
227256
name: "string".to_string(),
228257
is_nullable: true,
258+
json_annotations: None,
229259
},
230260
}],
231261
};

packages/go-gen/src/main.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,11 @@ pub fn build_enum_variant(
175175
);
176176
// we can unwrap here, because we checked the length above
177177
let (name, schema) = properties.first_key_value().unwrap();
178-
let GoType { name: ty, .. } = schema_object_type(
178+
let GoType {
179+
name: ty,
180+
json_annotations,
181+
..
182+
} = schema_object_type(
179183
schema.object()?,
180184
TypeContext::new(enum_name, name),
181185
additional_structs,
@@ -187,6 +191,7 @@ pub fn build_enum_variant(
187191
ty: GoType {
188192
name: ty,
189193
is_nullable: true, // always nullable
194+
json_annotations,
190195
},
191196
})
192197
}
@@ -524,4 +529,26 @@ mod tests {
524529
"#,
525530
);
526531
}
532+
533+
#[test]
534+
fn timestamp_works() {
535+
use cosmwasm_std::Timestamp;
536+
537+
#[cw_serde]
538+
struct A {
539+
a: Timestamp,
540+
b: Option<Timestamp>,
541+
}
542+
543+
let code = generate_go(cosmwasm_schema::schema_for!(A)).unwrap();
544+
assert_code_eq(
545+
code,
546+
r#"
547+
type A struct {
548+
A uint64 `json:"a,string"`
549+
B uint64 `json:"b,string,omitempty"`
550+
}
551+
"#,
552+
);
553+
}
527554
}

packages/go-gen/src/schema.rs

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ pub fn schema_object_type(
3939
let mut is_nullable = is_null(schema);
4040

4141
// if it has a title, use that
42-
let ty = if let Some(title) = schema.metadata.as_ref().and_then(|m| m.title.as_ref()) {
42+
let (ty, json_annotations) = if let Some(title) =
43+
schema.metadata.as_ref().and_then(|m| m.title.as_ref())
44+
{
4345
replace_custom_type(title)
4446
} else if let Some(reference) = &schema.reference {
4547
// if it has a reference, strip the path and use that
@@ -50,17 +52,25 @@ pub fn schema_object_type(
5052
.expect("split should always return at least one item"),
5153
))
5254
} else if let Some(t) = &schema.instance_type {
53-
type_from_instance_type(schema, type_context, t, additional_structs)?
55+
(
56+
type_from_instance_type(schema, type_context, t, additional_structs)?,
57+
None,
58+
)
5459
} else if let Some(subschemas) = schema.subschemas.as_ref().and_then(|s| s.any_of.as_ref()) {
5560
// check if one of them is null
56-
let nullable = nullable_type(subschemas)?;
61+
let nullable: Option<&SchemaObject> = nullable_type(subschemas)?;
5762
if let Some(non_null) = nullable {
5863
ensure!(subschemas.len() == 2, "multiple subschemas in anyOf");
5964
is_nullable = true;
6065
// extract non-null type
61-
let GoType { name, .. } =
62-
schema_object_type(non_null, type_context, additional_structs)?;
63-
replace_custom_type(&name)
66+
let GoType {
67+
name,
68+
json_annotations,
69+
..
70+
} = schema_object_type(non_null, type_context, additional_structs)?;
71+
// let (ty, annotations) = replace_custom_type(&name);
72+
// (ty, annotations.or(json_annotations))
73+
(name, json_annotations)
6474
} else {
6575
subschema_type(subschemas, type_context, additional_structs)
6676
.context("failed to get type of anyOf subschemas")?
@@ -79,6 +89,7 @@ pub fn schema_object_type(
7989
Ok(GoType {
8090
name: ty,
8191
is_nullable,
92+
json_annotations, // TODO: implement
8293
})
8394
}
8495

@@ -197,11 +208,11 @@ pub fn type_from_instance_type(
197208
// for nullable array item types, we have to use a pointer type, even for basic types,
198209
// so we can pass null as elements
199210
// otherwise they would just be omitted from the array
200-
replace_custom_type(&if item_type.is_nullable {
211+
if item_type.is_nullable {
201212
format!("[]*{}", item_type.name)
202213
} else {
203214
format!("[]{}", item_type.name)
204-
})
215+
}
205216
} else {
206217
unreachable!("instance type should be one of the above")
207218
})
@@ -233,7 +244,7 @@ pub fn subschema_type(
233244
subschemas: &[Schema],
234245
type_context: TypeContext,
235246
additional_structs: &mut Vec<GoStruct>,
236-
) -> Result<String> {
247+
) -> Result<(String, Option<String>)> {
237248
ensure!(
238249
subschemas.len() == 1,
239250
"multiple subschemas are not supported"
@@ -257,26 +268,27 @@ pub fn documentation(schema: &SchemaObject) -> Option<String> {
257268

258269
/// Maps special types to their Go equivalents.
259270
/// If the given type is not a special type, returns `None`.
260-
pub fn custom_type_of(ty: &str) -> Option<&str> {
271+
/// Otherwise, returns a tuple of the Go type name and additional json annotations.
272+
pub fn custom_type_of(ty: &str) -> Option<(&str, Option<&str>)> {
261273
match ty {
262-
"Uint64" => Some("string"),
263-
"Uint128" => Some("string"),
264-
"Int64" => Some("string"),
265-
"Int128" => Some("string"),
266-
"Binary" => Some("[]byte"),
267-
"HexBinary" => Some("Checksum"),
268-
"Addr" => Some("string"),
269-
"Decimal" => Some("string"),
270-
"Decimal256" => Some("string"),
271-
"SignedDecimal" => Some("string"),
272-
"SignedDecimal256" => Some("string"),
273-
"Timestamp" => Some("uint64"),
274+
"Uint64" => Some(("string", None)),
275+
"Uint128" => Some(("string", None)),
276+
"Int64" => Some(("string", None)),
277+
"Int128" => Some(("string", None)),
278+
"Binary" => Some(("[]byte", None)),
279+
"HexBinary" => Some(("Checksum", None)),
280+
"Addr" => Some(("string", None)),
281+
"Decimal" => Some(("string", None)),
282+
"Decimal256" => Some(("string", None)),
283+
"SignedDecimal" => Some(("string", None)),
284+
"SignedDecimal256" => Some(("string", None)),
285+
"Timestamp" => Some(("uint64", Some("string"))),
274286
_ => None,
275287
}
276288
}
277289

278-
pub fn replace_custom_type(ty: &str) -> String {
290+
pub fn replace_custom_type(ty: &str) -> (String, Option<String>) {
279291
custom_type_of(ty)
280-
.map(|ty| ty.to_string())
281-
.unwrap_or_else(|| ty.to_string())
292+
.map(|(ty, json_annotations)| (ty.to_string(), json_annotations.map(String::from)))
293+
.unwrap_or_else(|| (ty.to_string(), None))
282294
}

packages/go-gen/tests/cosmwasm_std__IbcMsg.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type Coin struct {
3030
type IBCTimeout struct {
3131
Block *IBCTimeoutBlock `json:"block,omitempty"` // in wasmvm, this does not have "omitempty"
3232
// Nanoseconds since UNIX epoch
33-
Timestamp uint64 `json:"timestamp,omitempty"` // wasmvm has a "string" in here too
33+
Timestamp uint64 `json:"timestamp,string,omitempty"` // wasmvm has a "string" in here too
3434
}
3535

3636
// IBCTimeoutBlock Height is a monotonically increasing data type

0 commit comments

Comments
 (0)