diff --git a/schemars/tests/default.rs b/schemars/tests/default.rs index c80860d..aabdde6 100644 --- a/schemars/tests/default.rs +++ b/schemars/tests/default.rs @@ -14,11 +14,19 @@ fn six() -> i32 { 6 } +fn custom_serialize(value: &MyStruct2, ser: S) -> Result +where + S: serde::Serializer, +{ + ser.collect_str(&format_args!("i:{} b:{}", value.my_int, value.my_bool)) +} + #[derive(Default, Deserialize, Serialize, JsonSchema, Debug)] #[serde(default)] pub struct MyStruct { pub my_int: i32, pub my_bool: bool, + #[serde(serialize_with = "custom_serialize")] pub my_struct2: MyStruct2, } diff --git a/schemars/tests/expected/default.json b/schemars/tests/expected/default.json index 3610b54..35d88f3 100644 --- a/schemars/tests/expected/default.json +++ b/schemars/tests/expected/default.json @@ -4,24 +4,21 @@ "type": "object", "properties": { "my_bool": { - "type": "boolean", - "default": false + "default": false, + "type": "boolean" }, "my_int": { + "default": 0, "type": "integer", - "format": "int32", - "default": 0 + "format": "int32" }, "my_struct2": { + "default": "i:0 b:false", "allOf": [ { "$ref": "#/definitions/MyStruct2" } - ], - "default": { - "my_bool": false, - "my_int": 0 - } + ] } }, "definitions": { @@ -29,13 +26,13 @@ "type": "object", "properties": { "my_bool": { - "type": "boolean", - "default": true + "default": true, + "type": "boolean" }, "my_int": { + "default": 6, "type": "integer", - "format": "int32", - "default": 6 + "format": "int32" } } } diff --git a/schemars/tests/expected/remote_derive.json b/schemars/tests/expected/remote_derive.json index 29a1c2c..d5a040f 100644 --- a/schemars/tests/expected/remote_derive.json +++ b/schemars/tests/expected/remote_derive.json @@ -4,8 +4,6 @@ "type": "object", "required": [ "command_line", - "system_cpu_time", - "user_cpu_time", "wall_time" ], "properties": { @@ -13,10 +11,26 @@ "type": "string" }, "system_cpu_time": { - "$ref": "#/definitions/DurationDef" + "default": { + "nanos": 0, + "secs": 0 + }, + "allOf": [ + { + "$ref": "#/definitions/DurationDef" + } + ] }, "user_cpu_time": { - "$ref": "#/definitions/DurationDef" + "default": { + "nanos": 0, + "secs": 0 + }, + "allOf": [ + { + "$ref": "#/definitions/DurationDef" + } + ] }, "wall_time": { "$ref": "#/definitions/DurationDef" diff --git a/schemars/tests/remote_derive.rs b/schemars/tests/remote_derive.rs index 7397360..74a2c8c 100644 --- a/schemars/tests/remote_derive.rs +++ b/schemars/tests/remote_derive.rs @@ -2,31 +2,40 @@ mod util; use other_crate::Duration; use schemars::JsonSchema; +use serde::Serialize; use util::*; mod other_crate { - #[derive(Debug)] + #[derive(Debug, Default)] pub struct Duration { pub secs: i64, pub nanos: i32, } } -#[derive(Debug, JsonSchema)] +#[derive(Debug, JsonSchema, Serialize)] #[serde(remote = "Duration")] struct DurationDef { secs: i64, nanos: i32, } -#[derive(Debug, JsonSchema)] +fn custom_serialize(value: &Duration, ser: S) -> Result +where + S: serde::Serializer, +{ + ser.collect_str(&format_args!("{}.{:09}s", value.secs, value.nanos)) +} + +#[derive(Debug, JsonSchema, Serialize)] struct Process { command_line: String, #[serde(with = "DurationDef")] wall_time: Duration, - #[serde(with = "DurationDef")] + #[serde(default, with = "DurationDef")] user_cpu_time: Duration, - #[serde(deserialize_with = "some_serialize_function")] + // FIXME this should serialize the default as "0.000000000s" + #[serde(default, serialize_with = "custom_serialize")] #[schemars(with = "DurationDef")] system_cpu_time: Duration, } diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index 28f643e..60fbadd 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -292,7 +292,6 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream let name = field.attrs.name().deserialize_name(); let ty = field.ty; - // TODO respect serialize_with on field let default = match field.attrs.default() { SerdeDefault::None if set_container_default.is_some() => { let field_ident = field @@ -305,7 +304,27 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream SerdeDefault::None => None, SerdeDefault::Default => Some(quote!(<#ty>::default())), SerdeDefault::Path(path) => Some(quote!(#path())), - }; + } + .map(|d| match field.attrs.serialize_with() { + Some(ser_with) => quote! { + { + struct _SchemarsDefaultSerialize(T); + + impl serde::Serialize for _SchemarsDefaultSerialize<#ty> + { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer + { + #ser_with(&self.0, serializer) + } + } + + _SchemarsDefaultSerialize(#d) + } + }, + None => d, + }); if default.is_none() { required.push(name.clone()); diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs index 519ef3a..b652eb9 100644 --- a/schemars_derive/src/metadata.rs +++ b/schemars_derive/src/metadata.rs @@ -55,9 +55,9 @@ pub fn set_metadata_on_schema(schema_expr: TokenStream, metadata: &SchemaMetadat if let Some(default) = &metadata.default { setters.push(quote! { - metadata.default = match serde_json::json!(#default) { - serde_json::value::Value::Null => None, - d => Some(d), + metadata.default = match serde_json::value::to_value(#default) { + Ok(serde_json::value::Value::Null) | Err(_) => None, + Ok(d) => Some(d), }; }) }