Skip to content

Commit 3c2e2bc

Browse files
authored
Merge pull request #49 from ImJeremyHe/jh/deny-unknown
feat: support deny_unknown_fields
2 parents 6be3f5a + ced86b1 commit 3c2e2bc

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed

Diff for: derives/src/container.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::symbol::{
2-
DEFAULT, NAME, ROOT, SKIP_SERIALIZING, TYPE, VEC_SIZE, WITH_CUSTOM_NS, WITH_NS, XML_SERDE,
2+
DEFAULT, DENY_UNKNOWN, NAME, ROOT, SKIP_SERIALIZING, TYPE, VEC_SIZE, WITH_CUSTOM_NS, WITH_NS,
3+
XML_SERDE,
34
};
45
use proc_macro2::{Group, Span, TokenStream, TokenTree};
56
use syn::parse::{self, Parse};
@@ -16,6 +17,7 @@ pub struct Container<'a> {
1617
pub with_ns: Option<syn::LitByteStr>,
1718
pub custom_ns: Vec<(syn::LitByteStr, syn::LitByteStr)>,
1819
pub root: Option<syn::LitByteStr>,
20+
pub deny_unknown: bool,
1921
}
2022

2123
impl<'a> Container<'a> {
@@ -27,12 +29,16 @@ impl<'a> Container<'a> {
2729
if self.root.is_some() && self.is_enum() {
2830
panic!("for clarity, enum should not have the root attribute. please use a struct to wrap the enum and set its type to untag")
2931
}
32+
if self.deny_unknown && self.is_enum() {
33+
panic!("`deny_unknown_fields` is not supported in enum type")
34+
}
3035
}
3136

3237
pub fn from_ast(item: &'a syn::DeriveInput, _derive: Derive) -> Container<'a> {
3338
let mut with_ns = Option::<syn::LitByteStr>::None;
3439
let mut custom_ns = Vec::<(syn::LitByteStr, syn::LitByteStr)>::new();
3540
let mut root = Option::<syn::LitByteStr>::None;
41+
let mut deny_unknown = false;
3642
for meta_item in item
3743
.attrs
3844
.iter()
@@ -49,6 +55,9 @@ impl<'a> Container<'a> {
4955
let s = get_lit_byte_str(&m.value).expect("parse root failed");
5056
root = Some(s.clone());
5157
}
58+
Meta::Path(p) if p == DENY_UNKNOWN => {
59+
deny_unknown = true;
60+
}
5261
Meta::List(l) if l.path == WITH_CUSTOM_NS => {
5362
let strs = l
5463
.parse_args_with(Punctuated::<syn::LitByteStr, Comma>::parse_terminated)
@@ -80,6 +89,7 @@ impl<'a> Container<'a> {
8089
with_ns,
8190
custom_ns,
8291
root,
92+
deny_unknown,
8393
}
8494
}
8595
syn::Data::Enum(e) => {
@@ -95,6 +105,7 @@ impl<'a> Container<'a> {
95105
with_ns,
96106
custom_ns,
97107
root,
108+
deny_unknown,
98109
}
99110
}
100111
syn::Data::Union(_) => panic!("Only support struct and enum type, union is found"),

Diff for: derives/src/de.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,21 @@ pub fn get_de_struct_impl_block(container: Container) -> proc_macro2::TokenStrea
169169
} else {
170170
quote! {}
171171
};
172+
let encounter_unknown = if container.deny_unknown {
173+
quote! {panic!("encoutnering unknown field: {_filed:?}")}
174+
} else {
175+
quote! {}
176+
};
177+
let encounter_unknown_branch = quote! {
178+
Ok(Event::Empty(_s)) => {
179+
let _field = _s.name().into_inner();
180+
#encounter_unknown
181+
}
182+
Ok(Event::Start(_s)) => {
183+
let _field = _s.name().into_inner();
184+
#encounter_unknown
185+
}
186+
};
172187
quote! {
173188
#[allow(unused_assignments)]
174189
impl #impl_generics ::xmlserde::XmlDeserialize for #ident #type_generics #where_clause {
@@ -183,7 +198,10 @@ pub fn get_de_struct_impl_block(container: Container) -> proc_macro2::TokenStrea
183198
if let Ok(attr) = attr {
184199
match attr.key.into_inner() {
185200
#(#attr_branches)*
186-
_ => {},
201+
_ => {
202+
let _field = attr.key.into_inner();
203+
#encounter_unknown;
204+
},
187205
}
188206
}
189207
});
@@ -199,6 +217,7 @@ pub fn get_de_struct_impl_block(container: Container) -> proc_macro2::TokenStrea
199217
#sfc_branch
200218
#child_branches
201219
#text_branch
220+
#encounter_unknown_branch
202221
Ok(Event::Eof) => break,
203222
Err(_) => break,
204223
_ => {},

Diff for: derives/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use syn::{Ident, Path};
44
#[derive(Copy, Clone)]
55
pub struct Symbol(&'static str);
66

7+
pub const DENY_UNKNOWN: Symbol = Symbol("deny_unknown_fields");
78
pub const WITH_NS: Symbol = Symbol("with_ns");
89
pub const WITH_CUSTOM_NS: Symbol = Symbol("with_custom_ns");
910
pub const ROOT: Symbol = Symbol("root");

Diff for: tests/lib.rs

+52
Original file line numberDiff line numberDiff line change
@@ -689,4 +689,56 @@ mod tests {
689689
let expect = xml_serialize(text_p);
690690
assert_eq!(expect, xml);
691691
}
692+
693+
#[test]
694+
#[should_panic]
695+
fn test_unknown_fields_in_struct_deny_unknown_attr() {
696+
#[derive(Debug, XmlSerialize, XmlDeserialize)]
697+
#[xmlserde(root = b"pet")]
698+
#[xmlserde(deny_unknown_fields)]
699+
pub struct Pet {
700+
#[xmlserde(ty = "attr", name = b"name")]
701+
pub name: String,
702+
}
703+
let xml = r#"<pet name="Chaplin" age="1"/>"#;
704+
let _ = xml_deserialize_from_str::<Pet>(&xml);
705+
}
706+
707+
#[test]
708+
fn test_unknown_fields_in_struct_accept_unknown_attr() {
709+
#[derive(Debug, XmlSerialize, XmlDeserialize)]
710+
#[xmlserde(root = b"pet")]
711+
pub struct Pet {
712+
#[xmlserde(ty = "attr", name = b"name")]
713+
pub name: String,
714+
}
715+
let xml = r#"<pet name="Chaplin" age="1"/>"#;
716+
let _ = xml_deserialize_from_str::<Pet>(&xml);
717+
}
718+
719+
#[test]
720+
#[should_panic]
721+
fn test_unknown_fields_in_struct_deny_unknown_field() {
722+
#[derive(Debug, XmlSerialize, XmlDeserialize)]
723+
#[xmlserde(root = b"pet")]
724+
#[xmlserde(deny_unknown_fields)]
725+
pub struct Pet {
726+
#[xmlserde(ty = "attr", name = b"name")]
727+
pub name: String,
728+
}
729+
let xml = r#"<pet name="Chaplin"><weight/></pet>"#;
730+
let _ = xml_deserialize_from_str::<Pet>(&xml);
731+
}
732+
733+
#[test]
734+
fn test_unknown_fields_in_struct_accept_unknown_field() {
735+
#[derive(Debug, XmlSerialize, XmlDeserialize)]
736+
#[xmlserde(root = b"pet")]
737+
pub struct Pet {
738+
#[xmlserde(ty = "attr", name = b"name")]
739+
pub name: String,
740+
}
741+
let xml = r#"<pet name="Chaplin"><weight/></pet>"#;
742+
let _ = xml_deserialize_from_str::<Pet>(&xml);
743+
}
692744
}

0 commit comments

Comments
 (0)