Skip to content

Commit 26f4711

Browse files
committed
Default impl generation can be broken
1 parent 0d30d24 commit 26f4711

File tree

3 files changed

+126
-13
lines changed

3 files changed

+126
-13
lines changed

typify-impl/src/value.rs

+23-13
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,25 @@ fn value_for_struct_props(
379379
) -> Option<Vec<TokenStream>> {
380380
let map = value.as_object()?;
381381

382+
let direct_props = properties.iter().filter_map(|prop| {
383+
let name = match &prop.rename {
384+
StructPropertyRename::None => &prop.name,
385+
StructPropertyRename::Rename(rename) => rename,
386+
StructPropertyRename::Flatten => return None,
387+
};
388+
389+
let name_ident = format_ident!("{}", &prop.name);
390+
391+
if let Some(value) = map.get(name) {
392+
let type_entry = type_space.id_to_entry.get(&prop.type_id).unwrap();
393+
let prop_value = type_entry.output_value(type_space, value, scope)?;
394+
395+
Some(quote! { #name_ident: #prop_value })
396+
} else {
397+
Some(quote! { #name_ident: Default::default() })
398+
}
399+
});
400+
382401
let prop_map = properties
383402
.iter()
384403
.filter_map(|prop| {
@@ -392,17 +411,6 @@ fn value_for_struct_props(
392411
})
393412
.collect::<BTreeMap<_, _>>();
394413

395-
let direct_props = map.iter().filter_map(|(name, value)| {
396-
// It's okay if the property isn't in the prop_map... it must be part
397-
// of one of the flattened properties.
398-
let prop = prop_map.get(name)?;
399-
let type_entry = type_space.id_to_entry.get(&prop.type_id).unwrap();
400-
let prop_value = type_entry.output_value(type_space, value, scope)?;
401-
let name_ident = format_ident!("{}", name);
402-
403-
Some(quote! { #name_ident: #prop_value })
404-
});
405-
406414
let extra_value = serde_json::Value::Object(
407415
map.clone()
408416
.into_iter()
@@ -624,7 +632,8 @@ mod tests {
624632
super::Test {
625633
a: "aaaa".to_string(),
626634
b: 7_u32,
627-
c: Some("cccc".to_string())
635+
c: Some("cccc".to_string()),
636+
d: Default::default()
628637
}
629638
}
630639
.to_string()
@@ -665,7 +674,8 @@ mod tests {
665674
Test {
666675
a: "aaaa".to_string(),
667676
b: 7_u32,
668-
c: Some("cccc".to_string())
677+
c: Some("cccc".to_string()),
678+
d: Default::default()
669679
}
670680
}
671681
.to_string()

typify/tests/schemas/types-with-defaults.json

+21
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,27 @@
2828
]
2929
}
3030
}
31+
},
32+
"OuterThing": {
33+
"type": "object",
34+
"properties": {
35+
"thing": {
36+
"type": "object",
37+
"title": "ThingWithDefaults",
38+
"properties": {
39+
"a": {
40+
"type": "string"
41+
},
42+
"type": {
43+
"type": "string"
44+
}
45+
},
46+
"additionalProperties": false,
47+
"default": {
48+
"type": "bee"
49+
}
50+
}
51+
}
3152
}
3253
}
3354
}

typify/tests/schemas/types-with-defaults.rs

+82
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,44 @@ pub mod error {
2626
}
2727
}
2828
}
29+
#[doc = "OuterThing"]
30+
#[doc = r""]
31+
#[doc = r" <details><summary>JSON schema</summary>"]
32+
#[doc = r""]
33+
#[doc = r" ```json"]
34+
#[doc = "{"]
35+
#[doc = " \"type\": \"object\","]
36+
#[doc = " \"properties\": {"]
37+
#[doc = " \"thing\": {"]
38+
#[doc = " \"title\": \"ThingWithDefaults\","]
39+
#[doc = " \"default\": {"]
40+
#[doc = " \"type\": \"bee\""]
41+
#[doc = " },"]
42+
#[doc = " \"type\": \"object\","]
43+
#[doc = " \"properties\": {"]
44+
#[doc = " \"a\": {"]
45+
#[doc = " \"type\": \"string\""]
46+
#[doc = " },"]
47+
#[doc = " \"type\": {"]
48+
#[doc = " \"type\": \"string\""]
49+
#[doc = " }"]
50+
#[doc = " },"]
51+
#[doc = " \"additionalProperties\": false"]
52+
#[doc = " }"]
53+
#[doc = " }"]
54+
#[doc = "}"]
55+
#[doc = r" ```"]
56+
#[doc = r" </details>"]
57+
#[derive(Clone, Debug, Deserialize, Serialize)]
58+
pub struct OuterThing {
59+
#[serde(default, skip_serializing_if = "Option::is_none")]
60+
pub thing: Option<ThingWithDefaults>,
61+
}
62+
impl From<&OuterThing> for OuterThing {
63+
fn from(value: &OuterThing) -> Self {
64+
value.clone()
65+
}
66+
}
2967
#[doc = "TestBed"]
3068
#[doc = r""]
3169
#[doc = r" <details><summary>JSON schema</summary>"]
@@ -72,6 +110,50 @@ impl From<&TestBed> for TestBed {
72110
value.clone()
73111
}
74112
}
113+
#[doc = "ThingWithDefaults"]
114+
#[doc = r""]
115+
#[doc = r" <details><summary>JSON schema</summary>"]
116+
#[doc = r""]
117+
#[doc = r" ```json"]
118+
#[doc = "{"]
119+
#[doc = " \"title\": \"ThingWithDefaults\","]
120+
#[doc = " \"default\": {"]
121+
#[doc = " \"type\": \"bee\""]
122+
#[doc = " },"]
123+
#[doc = " \"type\": \"object\","]
124+
#[doc = " \"properties\": {"]
125+
#[doc = " \"a\": {"]
126+
#[doc = " \"type\": \"string\""]
127+
#[doc = " },"]
128+
#[doc = " \"type\": {"]
129+
#[doc = " \"type\": \"string\""]
130+
#[doc = " }"]
131+
#[doc = " },"]
132+
#[doc = " \"additionalProperties\": false"]
133+
#[doc = "}"]
134+
#[doc = r" ```"]
135+
#[doc = r" </details>"]
136+
#[derive(Clone, Debug, Deserialize, Serialize)]
137+
#[serde(deny_unknown_fields)]
138+
pub struct ThingWithDefaults {
139+
#[serde(default, skip_serializing_if = "Option::is_none")]
140+
pub a: Option<String>,
141+
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
142+
pub type_: Option<String>,
143+
}
144+
impl From<&ThingWithDefaults> for ThingWithDefaults {
145+
fn from(value: &ThingWithDefaults) -> Self {
146+
value.clone()
147+
}
148+
}
149+
impl Default for ThingWithDefaults {
150+
fn default() -> Self {
151+
ThingWithDefaults {
152+
a: Default::default(),
153+
type_: Some("bee".to_string()),
154+
}
155+
}
156+
}
75157
#[doc = r" Generation of default values for serde."]
76158
pub mod defaults {
77159
pub(super) fn test_bed_any() -> Vec<serde_json::Value> {

0 commit comments

Comments
 (0)