diff --git a/typify-impl/src/value.rs b/typify-impl/src/value.rs index 4e9e13c8..d4dfd9d7 100644 --- a/typify-impl/src/value.rs +++ b/typify-impl/src/value.rs @@ -8,8 +8,9 @@ use quote::{format_ident, quote}; use crate::{ convert::STD_NUM_NONZERO_PREFIX, type_entry::{ - EnumTagType, StructProperty, StructPropertyRename, TypeEntry, TypeEntryDetails, - TypeEntryEnum, TypeEntryNative, TypeEntryNewtype, TypeEntryStruct, Variant, VariantDetails, + EnumTagType, StructProperty, StructPropertyRename, StructPropertyState, TypeEntry, + TypeEntryDetails, TypeEntryEnum, TypeEntryNative, TypeEntryNewtype, TypeEntryStruct, + Variant, VariantDetails, }, TypeId, TypeSpace, }; @@ -398,9 +399,18 @@ fn value_for_struct_props( let type_entry = type_space.id_to_entry.get(&prop.type_id).unwrap(); let prop_value = type_entry.output_value(type_space, value, scope)?; + Some(quote! { #name_ident: #prop_value }) + } else if let StructPropertyState::Default(default) = &prop.state { + // The property is absent from the whole-type default value, but it + // declares its own default. Use that so that `T::default()` agrees + // with deserializing the (partial) default value, which fills in + // absent properties via their serde default functions. + let type_entry = type_space.id_to_entry.get(&prop.type_id).unwrap(); + let prop_value = type_entry.output_value(type_space, &default.0, scope)?; + Some(quote! { #name_ident: #prop_value }) } else { - Some(quote! { #name_ident: Default::default() }) + Some(quote! { #name_ident: ::std::default::Default::default() }) } }); @@ -643,7 +653,7 @@ mod tests { a: "aaaa".to_string(), b: 7_u32, c: ::std::option::Option::Some("cccc".to_string()), - d: Default::default() + d: ::std::default::Default::default() } } .to_string() @@ -685,7 +695,7 @@ mod tests { a: "aaaa".to_string(), b: 7_u32, c: ::std::option::Option::Some("cccc".to_string()), - d: Default::default() + d: ::std::default::Default::default() } } .to_string() diff --git a/typify/tests/schemas/types-with-defaults.json b/typify/tests/schemas/types-with-defaults.json index f3666710..613e145b 100644 --- a/typify/tests/schemas/types-with-defaults.json +++ b/typify/tests/schemas/types-with-defaults.json @@ -86,6 +86,47 @@ } } }, + "SeparatorConfig": { + "type": "object", + "additionalProperties": false, + "default": {}, + "properties": { + "lineThickness": { + "type": "integer", + "default": 1 + }, + "lineColor": { + "type": [ + "string", + "null" + ], + "default": "#B2000000" + } + } + }, + "SeparatorHolder": { + "type": "object", + "properties": { + "separatorAll": { + "default": {}, + "allOf": [ + { + "$ref": "#/definitions/SeparatorConfig" + } + ] + }, + "separatorSome": { + "default": { + "lineThickness": 5 + }, + "allOf": [ + { + "$ref": "#/definitions/SeparatorConfig" + } + ] + } + } + }, "UInt": { "type": "integer" }, diff --git a/typify/tests/schemas/types-with-defaults.rs b/typify/tests/schemas/types-with-defaults.rs index 383ece76..9f2db94d 100644 --- a/typify/tests/schemas/types-with-defaults.rs +++ b/typify/tests/schemas/types-with-defaults.rs @@ -160,6 +160,111 @@ impl OuterThing { Default::default() } } +#[doc = "`SeparatorConfig`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"default\": {},"] +#[doc = " \"type\": \"object\","] +#[doc = " \"properties\": {"] +#[doc = " \"lineColor\": {"] +#[doc = " \"default\": \"#B2000000\","] +#[doc = " \"type\": ["] +#[doc = " \"string\","] +#[doc = " \"null\""] +#[doc = " ]"] +#[doc = " },"] +#[doc = " \"lineThickness\": {"] +#[doc = " \"default\": 1,"] +#[doc = " \"type\": \"integer\""] +#[doc = " }"] +#[doc = " },"] +#[doc = " \"additionalProperties\": false"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +#[serde(deny_unknown_fields)] +pub struct SeparatorConfig { + #[serde( + rename = "lineColor", + default = "defaults::separator_config_line_color" + )] + pub line_color: ::std::option::Option<::std::string::String>, + #[serde(rename = "lineThickness", default = "defaults::default_u64::")] + pub line_thickness: i64, +} +impl ::std::default::Default for SeparatorConfig { + fn default() -> Self { + Self { + line_color: defaults::separator_config_line_color(), + line_thickness: defaults::default_u64::(), + } + } +} +impl SeparatorConfig { + pub fn builder() -> builder::SeparatorConfig { + Default::default() + } +} +#[doc = "`SeparatorHolder`"] +#[doc = r""] +#[doc = r"
JSON schema"] +#[doc = r""] +#[doc = r" ```json"] +#[doc = "{"] +#[doc = " \"type\": \"object\","] +#[doc = " \"properties\": {"] +#[doc = " \"separatorAll\": {"] +#[doc = " \"default\": {},"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"$ref\": \"#/definitions/SeparatorConfig\""] +#[doc = " }"] +#[doc = " ]"] +#[doc = " },"] +#[doc = " \"separatorSome\": {"] +#[doc = " \"default\": {"] +#[doc = " \"lineThickness\": 5"] +#[doc = " },"] +#[doc = " \"allOf\": ["] +#[doc = " {"] +#[doc = " \"$ref\": \"#/definitions/SeparatorConfig\""] +#[doc = " }"] +#[doc = " ]"] +#[doc = " }"] +#[doc = " }"] +#[doc = "}"] +#[doc = r" ```"] +#[doc = r"
"] +#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)] +pub struct SeparatorHolder { + #[serde( + rename = "separatorAll", + default = "defaults::separator_holder_separator_all" + )] + pub separator_all: SeparatorConfig, + #[serde( + rename = "separatorSome", + default = "defaults::separator_holder_separator_some" + )] + pub separator_some: SeparatorConfig, +} +impl ::std::default::Default for SeparatorHolder { + fn default() -> Self { + Self { + separator_all: defaults::separator_holder_separator_all(), + separator_some: defaults::separator_holder_separator_some(), + } + } +} +impl SeparatorHolder { + pub fn builder() -> builder::SeparatorHolder { + Default::default() + } +} #[doc = "`TestBed`"] #[doc = r""] #[doc = r"
JSON schema"] @@ -252,7 +357,7 @@ pub struct ThingWithDefaults { impl ::std::default::Default for ThingWithDefaults { fn default() -> Self { ThingWithDefaults { - a: Default::default(), + a: ::std::default::Default::default(), type_: ::std::option::Option::Some("bee".to_string()), } } @@ -505,6 +610,117 @@ pub mod builder { } } #[derive(Clone, Debug)] + pub struct SeparatorConfig { + line_color: ::std::result::Result< + ::std::option::Option<::std::string::String>, + ::std::string::String, + >, + line_thickness: ::std::result::Result, + } + impl ::std::default::Default for SeparatorConfig { + fn default() -> Self { + Self { + line_color: Ok(super::defaults::separator_config_line_color()), + line_thickness: Ok(super::defaults::default_u64::()), + } + } + } + impl SeparatorConfig { + pub fn line_color(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>, + T::Error: ::std::fmt::Display, + { + self.line_color = value + .try_into() + .map_err(|e| format!("error converting supplied value for line_color: {e}")); + self + } + pub fn line_thickness(mut self, value: T) -> Self + where + T: ::std::convert::TryInto, + T::Error: ::std::fmt::Display, + { + self.line_thickness = value + .try_into() + .map_err(|e| format!("error converting supplied value for line_thickness: {e}")); + self + } + } + impl ::std::convert::TryFrom for super::SeparatorConfig { + type Error = super::error::ConversionError; + fn try_from( + value: SeparatorConfig, + ) -> ::std::result::Result { + Ok(Self { + line_color: value.line_color?, + line_thickness: value.line_thickness?, + }) + } + } + impl ::std::convert::From for SeparatorConfig { + fn from(value: super::SeparatorConfig) -> Self { + Self { + line_color: Ok(value.line_color), + line_thickness: Ok(value.line_thickness), + } + } + } + #[derive(Clone, Debug)] + pub struct SeparatorHolder { + separator_all: ::std::result::Result, + separator_some: ::std::result::Result, + } + impl ::std::default::Default for SeparatorHolder { + fn default() -> Self { + Self { + separator_all: Ok(super::defaults::separator_holder_separator_all()), + separator_some: Ok(super::defaults::separator_holder_separator_some()), + } + } + } + impl SeparatorHolder { + pub fn separator_all(mut self, value: T) -> Self + where + T: ::std::convert::TryInto, + T::Error: ::std::fmt::Display, + { + self.separator_all = value + .try_into() + .map_err(|e| format!("error converting supplied value for separator_all: {e}")); + self + } + pub fn separator_some(mut self, value: T) -> Self + where + T: ::std::convert::TryInto, + T::Error: ::std::fmt::Display, + { + self.separator_some = value + .try_into() + .map_err(|e| format!("error converting supplied value for separator_some: {e}")); + self + } + } + impl ::std::convert::TryFrom for super::SeparatorHolder { + type Error = super::error::ConversionError; + fn try_from( + value: SeparatorHolder, + ) -> ::std::result::Result { + Ok(Self { + separator_all: value.separator_all?, + separator_some: value.separator_some?, + }) + } + } + impl ::std::convert::From for SeparatorHolder { + fn from(value: super::SeparatorHolder) -> Self { + Self { + separator_all: Ok(value.separator_all), + separator_some: Ok(value.separator_some), + } + } + } + #[derive(Clone, Debug)] pub struct TestBed { any: ::std::result::Result<::std::vec::Vec<::serde_json::Value>, ::std::string::String>, id: ::std::result::Result<::uuid::Uuid, ::std::string::String>, @@ -659,6 +875,13 @@ pub mod builder { } #[doc = r" Generation of default values for serde."] pub mod defaults { + pub(super) fn default_u64() -> T + where + T: ::std::convert::TryFrom, + >::Error: ::std::fmt::Debug, + { + T::try_from(V).unwrap() + } pub(super) fn default_nzu64() -> T where T: ::std::convert::TryFrom<::std::num::NonZeroU64>, @@ -676,6 +899,21 @@ pub mod defaults { { ::std::option::Option::Some(::std::num::NonZeroU64::new(1).unwrap()) } + pub(super) fn separator_config_line_color() -> ::std::option::Option<::std::string::String> { + ::std::option::Option::Some("#B2000000".to_string()) + } + pub(super) fn separator_holder_separator_all() -> super::SeparatorConfig { + super::SeparatorConfig { + line_color: ::std::option::Option::Some("#B2000000".to_string()), + line_thickness: 1_i64, + } + } + pub(super) fn separator_holder_separator_some() -> super::SeparatorConfig { + super::SeparatorConfig { + line_color: ::std::option::Option::Some("#B2000000".to_string()), + line_thickness: 5_i64, + } + } pub(super) fn test_bed_any() -> ::std::vec::Vec<::serde_json::Value> { vec![ ::serde_json::from_str::<::serde_json::Value>("[8,6,7]").unwrap(),