diff --git a/schemars/tests/default.rs b/schemars/tests/default.rs index 77ebe77..c80860d 100644 --- a/schemars/tests/default.rs +++ b/schemars/tests/default.rs @@ -31,7 +31,6 @@ pub struct MyStruct2 { } #[test] -#[ignore] // not yet implemented (https://github.com/GREsau/schemars/issues/6) fn schema_default_values() -> TestResult { test_default_generated_schema::("default") } diff --git a/schemars/tests/expected/schema-openapi3.json b/schemars/tests/expected/schema-openapi3.json index 7833836..925f4ae 100644 --- a/schemars/tests/expected/schema-openapi3.json +++ b/schemars/tests/expected/schema-openapi3.json @@ -72,6 +72,7 @@ }, "definitions": { "description": "The `$defs` keyword.\n\nThis is currently serialized as `definitions` for backwards compatibility.\n\nSee [JSON Schema 8.2.5. Schema Re-Use With \"$defs\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/components/schemas/Schema" @@ -79,6 +80,7 @@ }, "deprecated": { "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", + "default": false, "type": "boolean" }, "description": { @@ -220,6 +222,7 @@ }, "patternProperties": { "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/components/schemas/Schema" @@ -227,6 +230,7 @@ }, "properties": { "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/components/schemas/Schema" @@ -243,10 +247,12 @@ }, "readOnly": { "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" }, "required": { "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", + "default": [], "type": "array", "items": { "type": "string" @@ -282,6 +288,7 @@ }, "writeOnly": { "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" } }, @@ -383,6 +390,7 @@ }, "deprecated": { "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", + "default": false, "type": "boolean" }, "description": { @@ -524,6 +532,7 @@ }, "patternProperties": { "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/components/schemas/Schema" @@ -531,6 +540,7 @@ }, "properties": { "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/components/schemas/Schema" @@ -547,10 +557,12 @@ }, "readOnly": { "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" }, "required": { "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", + "default": [], "type": "array", "items": { "type": "string" @@ -586,6 +598,7 @@ }, "writeOnly": { "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" } }, diff --git a/schemars/tests/expected/schema.json b/schemars/tests/expected/schema.json index 4982d5a..3a1f2e4 100644 --- a/schemars/tests/expected/schema.json +++ b/schemars/tests/expected/schema.json @@ -86,6 +86,7 @@ }, "definitions": { "description": "The `$defs` keyword.\n\nThis is currently serialized as `definitions` for backwards compatibility.\n\nSee [JSON Schema 8.2.5. Schema Re-Use With \"$defs\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/definitions/Schema" @@ -93,6 +94,7 @@ }, "deprecated": { "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", + "default": false, "type": "boolean" }, "description": { @@ -274,6 +276,7 @@ }, "patternProperties": { "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/definitions/Schema" @@ -281,6 +284,7 @@ }, "properties": { "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/definitions/Schema" @@ -299,10 +303,12 @@ }, "readOnly": { "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" }, "required": { "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", + "default": [], "type": "array", "items": { "type": "string" @@ -346,6 +352,7 @@ }, "writeOnly": { "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" } }, @@ -459,6 +466,7 @@ }, "deprecated": { "description": "The `deprecated` keyword.\n\nSee [JSON Schema Validation 9.3. \"deprecated\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).", + "default": false, "type": "boolean" }, "description": { @@ -640,6 +648,7 @@ }, "patternProperties": { "description": "The `patternProperties` keyword.\n\nSee [JSON Schema 9.3.2.2. \"patternProperties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/definitions/Schema" @@ -647,6 +656,7 @@ }, "properties": { "description": "The `properties` keyword.\n\nSee [JSON Schema 9.3.2.1. \"properties\"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).", + "default": {}, "type": "object", "additionalProperties": { "$ref": "#/definitions/Schema" @@ -665,10 +675,12 @@ }, "readOnly": { "description": "The `readOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" }, "required": { "description": "The `required` keyword.\n\nSee [JSON Schema Validation 6.5.3. \"required\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).", + "default": [], "type": "array", "items": { "type": "string" @@ -712,6 +724,7 @@ }, "writeOnly": { "description": "The `writeOnly` keyword.\n\nSee [JSON Schema Validation 9.4. \"readOnly\" and \"writeOnly\"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).", + "default": false, "type": "boolean" } }, diff --git a/schemars/tests/expected/skip_struct_fields.json b/schemars/tests/expected/skip_struct_fields.json index 3e78e1d..d238b7b 100644 --- a/schemars/tests/expected/skip_struct_fields.json +++ b/schemars/tests/expected/skip_struct_fields.json @@ -11,6 +11,7 @@ "type": "null" }, "readable": { + "default": "", "readOnly": true, "type": "string" }, diff --git a/schemars/tests/flatten.rs b/schemars/tests/flatten.rs index 304f70c..e445f34 100644 --- a/schemars/tests/flatten.rs +++ b/schemars/tests/flatten.rs @@ -38,6 +38,7 @@ struct Deep3 { #[derive(Debug, JsonSchema)] struct Deep4 { + #[serde(default)] os: &'static str, } diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index 8f244f1..28f643e 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -281,11 +281,33 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream .filter(|f| !f.attrs.skip_deserializing() || !f.attrs.skip_serializing()) .partition(|f| f.attrs.flatten()); - let container_has_default = has_default(cattrs.default()); + let set_container_default = match cattrs.default() { + SerdeDefault::None => None, + SerdeDefault::Default => Some(quote!(let cdefault = Self::default();)), + SerdeDefault::Path(path) => Some(quote!(let cdefault = #path();)), + }; + let mut required = Vec::new(); let recurse = nested.iter().map(|field| { let name = field.attrs.name().deserialize_name(); - if !container_has_default && !has_default(field.attrs.default()) { + 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 + .original + .ident + .as_ref() + .expect("This is not a tuple struct, so field should be named"); + Some(quote!(cdefault.#field_ident)) + } + SerdeDefault::None => None, + SerdeDefault::Default => Some(quote!(<#ty>::default())), + SerdeDefault::Path(path) => Some(quote!(#path())), + }; + + if default.is_none() { required.push(name.clone()); } @@ -298,6 +320,7 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream let metadata = SchemaMetadata { read_only: field.attrs.skip_deserializing(), write_only: field.attrs.skip_serializing(), + default, ..get_metadata_from_docs(&field.original.attrs) }; let schema_expr = set_metadata_on_schema(schema_expr, &metadata); @@ -332,14 +355,10 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream }); quote! { - #schema #(#flattens)* - } -} - -fn has_default(d: &SerdeDefault) -> bool { - match d { - SerdeDefault::None => false, - _ => true, + { + #set_container_default + #schema #(#flattens)* + } } } diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs index 61b1d4c..519ef3a 100644 --- a/schemars_derive/src/metadata.rs +++ b/schemars_derive/src/metadata.rs @@ -2,12 +2,13 @@ use crate::doc_attrs; use proc_macro2::TokenStream; use syn::Attribute; -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, Default)] pub struct SchemaMetadata { pub title: Option, pub description: Option, pub read_only: bool, pub write_only: bool, + pub default: Option, } pub fn set_metadata_on_schema_from_docs( @@ -40,6 +41,7 @@ pub fn set_metadata_on_schema(schema_expr: TokenStream, metadata: &SchemaMetadat metadata.description = Some(#description.to_owned()); }) } + if metadata.read_only { setters.push(quote! { metadata.read_only = true; @@ -51,6 +53,15 @@ 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), + }; + }) + } + if setters.is_empty() { return schema_expr; }