Skip to content

Commit

Permalink
fest(serde-rs#1587): implement serde(strict_or_some_other_name) attri…
Browse files Browse the repository at this point in the history
…bute
  • Loading branch information
Toromyx committed Oct 28, 2023
1 parent edb1a58 commit a3c11f3
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 9 deletions.
23 changes: 14 additions & 9 deletions serde_derive/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -961,10 +961,10 @@ fn deserialize_struct(
let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs);

// untagged struct variants do not get a visit_seq method. The same applies to
// structs that only have a map representation.
// structs that only have a map representation or are deserialized strict_or_some_other_name_ly.
let visit_seq = match form {
StructForm::Untagged(..) => None,
_ if cattrs.has_flatten() => None,
_ if cattrs.has_flatten() || cattrs.is_strict_or_some_other_name() => None,
_ => {
let mut_seq = if field_names_idents.is_empty() {
quote!(_)
Expand Down Expand Up @@ -1090,8 +1090,8 @@ fn deserialize_struct_in_place(
cattrs: &attr::Container,
) -> Option<Fragment> {
// for now we do not support in_place deserialization for structs that
// are represented as map.
if cattrs.has_flatten() {
// are represented as map or are deserialized strict_or_some_other_name_ly.
if cattrs.has_flatten() || cattrs.is_strict_or_some_other_name() {
return None;
}

Expand Down Expand Up @@ -2001,6 +2001,7 @@ fn deserialize_generated_identifier(
None,
!is_variant && cattrs.has_flatten(),
None,
cattrs.is_strict_or_some_other_name(),
));

let lifetime = if !is_variant && cattrs.has_flatten() {
Expand Down Expand Up @@ -2155,6 +2156,7 @@ fn deserialize_custom_identifier(
fallthrough_borrowed,
false,
cattrs.expecting(),
false,
));

quote_block! {
Expand Down Expand Up @@ -2188,6 +2190,7 @@ fn deserialize_identifier(
fallthrough_borrowed: Option<TokenStream>,
collect_other_fields: bool,
expecting: Option<&str>,
is_strict_or_some_other_name: bool,
) -> Fragment {
let str_mapping = fields.iter().map(|(_, ident, aliases)| {
// `aliases` also contains a main name
Expand Down Expand Up @@ -2255,7 +2258,7 @@ fn deserialize_identifier(
};

let visit_other = if collect_other_fields {
quote! {
Some(quote! {
fn visit_bool<__E>(self, __value: bool) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
Expand Down Expand Up @@ -2346,8 +2349,8 @@ fn deserialize_identifier(
{
_serde::__private::Ok(__Field::__other(_serde::__private::de::Content::Unit))
}
}
} else {
})
} else if !is_strict_or_some_other_name {
let u64_mapping = fields.iter().enumerate().map(|(i, (_, ident, _))| {
let i = i as u64;
quote!(#i => _serde::__private::Ok(#this_value::#ident))
Expand All @@ -2368,7 +2371,7 @@ fn deserialize_identifier(
&u64_fallthrough_arm_tokens
};

quote! {
Some(quote! {
fn visit_u64<__E>(self, __value: u64) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
Expand All @@ -2378,7 +2381,9 @@ fn deserialize_identifier(
_ => #u64_fallthrough_arm,
}
}
}
})
} else {
None
};

let visit_borrowed = if fallthrough_borrowed.is_some() || collect_other_fields {
Expand Down
25 changes: 25 additions & 0 deletions serde_derive/src/internals/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub struct Container {
rename_all_fields_rules: RenameAllRules,
ser_bound: Option<Vec<syn::WherePredicate>>,
de_bound: Option<Vec<syn::WherePredicate>>,
is_strict_or_some_other_name: bool,
tag: TagType,
type_from: Option<syn::Type>,
type_try_from: Option<syn::Type>,
Expand Down Expand Up @@ -296,6 +297,7 @@ impl Container {
let mut rename_all_fields_de_rule = Attr::none(cx, RENAME_ALL_FIELDS);
let mut ser_bound = Attr::none(cx, BOUND);
let mut de_bound = Attr::none(cx, BOUND);
let mut strict_or_some_other_name = BoolAttr::none(cx, STRICT_OR_SOME_OTHER_NAME);
let mut untagged = BoolAttr::none(cx, UNTAGGED);
let mut internal_tag = Attr::none(cx, TAG);
let mut content = Attr::none(cx, CONTENT);
Expand Down Expand Up @@ -446,6 +448,24 @@ impl Container {
let (ser, de) = get_where_predicates(cx, &meta)?;
ser_bound.set_opt(&meta.path, ser);
de_bound.set_opt(&meta.path, de);
} else if meta.path == STRICT_OR_SOME_OTHER_NAME {
// #[serde(strict_or_some_other_name)]
let msg = "#[serde(strict_or_some_other_name)] can only be used on structs with named fields";
match &item.data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
match fields {
syn::Fields::Named(_) => {
strict_or_some_other_name.set_true(&meta.path);
}
_ => {
cx.syn_error(meta.error(msg));
}
};
}
_ => {
cx.syn_error(meta.error(msg));
}
}
} else if meta.path == UNTAGGED {
// #[serde(untagged)]
match item.data {
Expand Down Expand Up @@ -581,6 +601,7 @@ impl Container {
},
ser_bound: ser_bound.get(),
de_bound: de_bound.get(),
is_strict_or_some_other_name: strict_or_some_other_name.get(),
tag: decide_tag(cx, item, untagged, internal_tag, content),
type_from: type_from.get(),
type_try_from: type_try_from.get(),
Expand Down Expand Up @@ -627,6 +648,10 @@ impl Container {
self.de_bound.as_ref().map(|vec| &vec[..])
}

pub fn is_strict_or_some_other_name(&self) -> bool {
self.is_strict_or_some_other_name
}

pub fn tag(&self) -> &TagType {
&self.tag
}
Expand Down
2 changes: 2 additions & 0 deletions serde_derive/src/internals/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub const SKIP: Symbol = Symbol("skip");
pub const SKIP_DESERIALIZING: Symbol = Symbol("skip_deserializing");
pub const SKIP_SERIALIZING: Symbol = Symbol("skip_serializing");
pub const SKIP_SERIALIZING_IF: Symbol = Symbol("skip_serializing_if");

pub const STRICT_OR_SOME_OTHER_NAME: Symbol = Symbol("strict_or_some_other_name");
pub const TAG: Symbol = Symbol("tag");
pub const TRANSPARENT: Symbol = Symbol("transparent");
pub const TRY_FROM: Symbol = Symbol("try_from");
Expand Down
35 changes: 35 additions & 0 deletions test_suite/tests/test_de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ struct StructDefault<T> {
b: T,
}

#[derive(PartialEq, Debug, Deserialize)]
struct StructNonStrictOrSomeOtherName {
a: i32,
}

#[derive(PartialEq, Debug, Deserialize)]
#[serde(strict_or_some_other_name)]
struct StructStrictOrSomeOtherName {
a: i32,
}

impl Default for StructDefault<String> {
fn default() -> Self {
StructDefault {
Expand Down Expand Up @@ -1592,6 +1603,30 @@ fn test_struct_default() {
);
}

#[test]
fn test_struct_non_strict_or_some_other_name() {
test(
StructNonStrictOrSomeOtherName { a: 50 },
&[Token::Seq { len: Some(1) }, Token::I32(50), Token::SeqEnd],
);
}

#[test]
fn test_struct_strict_or_some_other_name() {
test(
StructStrictOrSomeOtherName { a: 50 },
&[
Token::Struct {
name: "StructStrictOrSomeOtherName",
len: 1,
},
Token::Str("a"),
Token::I32(50),
Token::StructEnd,
],
);
}

#[test]
fn test_enum_unit() {
test(
Expand Down
14 changes: 14 additions & 0 deletions test_suite/tests/test_de_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ struct StructSkipAllDenyUnknown {
a: i32,
}

#[derive(PartialEq, Debug, Deserialize)]
#[serde(strict_or_some_other_name)]
struct StructStrictOrSomeOtherName {
a: i32,
}

#[derive(Default, PartialEq, Debug)]
struct NotDeserializable;

Expand Down Expand Up @@ -1179,6 +1185,14 @@ fn test_skip_all_deny_unknown() {
);
}

#[test]
fn test_strict_or_some_other_name() {
assert_de_tokens_error::<StructStrictOrSomeOtherName>(
&[Token::Seq { len: Some(1) }],
"invalid type: sequence, expected struct StructStrictOrSomeOtherName",
);
}

#[test]
fn test_unknown_variant() {
assert_de_tokens_error::<Enum>(
Expand Down
9 changes: 9 additions & 0 deletions test_suite/tests/ui/strict-or-some-other-name/enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use serde_derive::Deserialize;

#[derive(Deserialize)]
#[serde(strict_or_some_other_name)]
enum E {
S { a: u8 },
}

fn main() {}
5 changes: 5 additions & 0 deletions test_suite/tests/ui/strict-or-some-other-name/enum.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: #[serde(strict_or_some_other_name)] can only be used on structs with named fields
--> tests/ui/strict-or-some-other-name/enum.rs:4:9
|
4 | #[serde(strict_or_some_other_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde_derive::Deserialize;

#[derive(Deserialize)]
#[serde(strict_or_some_other_name)]
struct S(u8);

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: #[serde(strict_or_some_other_name)] can only be used on structs with named fields
--> tests/ui/strict-or-some-other-name/newtype-struct.rs:4:9
|
4 | #[serde(strict_or_some_other_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
7 changes: 7 additions & 0 deletions test_suite/tests/ui/strict-or-some-other-name/tuple-struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde_derive::Deserialize;

#[derive(Deserialize)]
#[serde(strict_or_some_other_name)]
struct S(u8, u8);

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: #[serde(strict_or_some_other_name)] can only be used on structs with named fields
--> tests/ui/strict-or-some-other-name/tuple-struct.rs:4:9
|
4 | #[serde(strict_or_some_other_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
7 changes: 7 additions & 0 deletions test_suite/tests/ui/strict-or-some-other-name/unit-struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde_derive::Deserialize;

#[derive(Deserialize)]
#[serde(strict_or_some_other_name)]
struct S;

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: #[serde(strict_or_some_other_name)] can only be used on structs with named fields
--> tests/ui/strict-or-some-other-name/unit-struct.rs:4:9
|
4 | #[serde(strict_or_some_other_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^

0 comments on commit a3c11f3

Please sign in to comment.