mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-06-15 03:51:03 +00:00
Add #[serde(rename_all_fields = "foo")] attribute
This commit is contained in:
@@ -88,9 +88,12 @@ impl<'a> Container<'a> {
|
|||||||
if field.attrs.flatten() {
|
if field.attrs.flatten() {
|
||||||
has_flatten = true;
|
has_flatten = true;
|
||||||
}
|
}
|
||||||
field
|
field.attrs.rename_by_rules(
|
||||||
|
&variant
|
||||||
.attrs
|
.attrs
|
||||||
.rename_by_rules(variant.attrs.rename_all_rules());
|
.rename_all_rules()
|
||||||
|
.or(attrs.rename_all_fields_rules()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,11 +193,23 @@ impl Name {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct RenameAllRules {
|
pub struct RenameAllRules {
|
||||||
serialize: RenameRule,
|
serialize: RenameRule,
|
||||||
deserialize: RenameRule,
|
deserialize: RenameRule,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RenameAllRules {
|
||||||
|
/// Returns a new `RenameAllRules` with the individual rules of `self` and
|
||||||
|
/// `other_rules` joined by `RenameRules::or`.
|
||||||
|
pub fn or(&self, other_rules: &Self) -> Self {
|
||||||
|
Self {
|
||||||
|
serialize: self.serialize.or(&other_rules.serialize),
|
||||||
|
deserialize: self.deserialize.or(&other_rules.deserialize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents struct or enum attribute information.
|
/// Represents struct or enum attribute information.
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
name: Name,
|
name: Name,
|
||||||
@@ -205,6 +217,7 @@ pub struct Container {
|
|||||||
deny_unknown_fields: bool,
|
deny_unknown_fields: bool,
|
||||||
default: Default,
|
default: Default,
|
||||||
rename_all_rules: RenameAllRules,
|
rename_all_rules: RenameAllRules,
|
||||||
|
rename_all_fields_rules: RenameAllRules,
|
||||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
tag: TagType,
|
tag: TagType,
|
||||||
@@ -288,6 +301,8 @@ impl Container {
|
|||||||
let mut default = Attr::none(cx, DEFAULT);
|
let mut default = Attr::none(cx, DEFAULT);
|
||||||
let mut rename_all_ser_rule = Attr::none(cx, RENAME_ALL);
|
let mut rename_all_ser_rule = Attr::none(cx, RENAME_ALL);
|
||||||
let mut rename_all_de_rule = Attr::none(cx, RENAME_ALL);
|
let mut rename_all_de_rule = Attr::none(cx, RENAME_ALL);
|
||||||
|
let mut rename_all_fields_ser_rule = Attr::none(cx, RENAME_ALL_FIELDS);
|
||||||
|
let mut rename_all_fields_de_rule = Attr::none(cx, RENAME_ALL_FIELDS);
|
||||||
let mut ser_bound = Attr::none(cx, BOUND);
|
let mut ser_bound = Attr::none(cx, BOUND);
|
||||||
let mut de_bound = Attr::none(cx, BOUND);
|
let mut de_bound = Attr::none(cx, BOUND);
|
||||||
let mut untagged = BoolAttr::none(cx, UNTAGGED);
|
let mut untagged = BoolAttr::none(cx, UNTAGGED);
|
||||||
@@ -341,6 +356,44 @@ impl Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if meta.path == RENAME_ALL_FIELDS {
|
||||||
|
// #[serde(rename_all_fields = "foo")]
|
||||||
|
// #[serde(rename_all_fields(serialize = "foo", deserialize = "bar"))]
|
||||||
|
let one_name = meta.input.peek(Token![=]);
|
||||||
|
let (ser, de) = get_renames(cx, RENAME_ALL_FIELDS, &meta)?;
|
||||||
|
|
||||||
|
match item.data {
|
||||||
|
syn::Data::Enum(_) => {
|
||||||
|
if let Some(ser) = ser {
|
||||||
|
match RenameRule::from_str(&ser.value()) {
|
||||||
|
Ok(rename_rule) => {
|
||||||
|
rename_all_fields_ser_rule.set(&meta.path, rename_rule);
|
||||||
|
}
|
||||||
|
Err(err) => cx.error_spanned_by(ser, err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(de) = de {
|
||||||
|
match RenameRule::from_str(&de.value()) {
|
||||||
|
Ok(rename_rule) => {
|
||||||
|
rename_all_fields_de_rule.set(&meta.path, rename_rule);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
if !one_name {
|
||||||
|
cx.error_spanned_by(de, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::Data::Struct(_) => {
|
||||||
|
let msg = "#[serde(rename_all_fields)] can only be used on enums";
|
||||||
|
cx.error_spanned_by(&meta.path, msg);
|
||||||
|
}
|
||||||
|
syn::Data::Union(_) => {
|
||||||
|
let msg = "#[serde(rename_all_fields)] can only be used on enums";
|
||||||
|
cx.error_spanned_by(&meta.path, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if meta.path == TRANSPARENT {
|
} else if meta.path == TRANSPARENT {
|
||||||
// #[serde(transparent)]
|
// #[serde(transparent)]
|
||||||
transparent.set_true(meta.path);
|
transparent.set_true(meta.path);
|
||||||
@@ -528,6 +581,10 @@ impl Container {
|
|||||||
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
|
serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
|
||||||
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
|
deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
|
||||||
},
|
},
|
||||||
|
rename_all_fields_rules: RenameAllRules {
|
||||||
|
serialize: rename_all_fields_ser_rule.get().unwrap_or(RenameRule::None),
|
||||||
|
deserialize: rename_all_fields_de_rule.get().unwrap_or(RenameRule::None),
|
||||||
|
},
|
||||||
ser_bound: ser_bound.get(),
|
ser_bound: ser_bound.get(),
|
||||||
de_bound: de_bound.get(),
|
de_bound: de_bound.get(),
|
||||||
tag: decide_tag(cx, item, untagged, internal_tag, content),
|
tag: decide_tag(cx, item, untagged, internal_tag, content),
|
||||||
@@ -551,6 +608,10 @@ impl Container {
|
|||||||
&self.rename_all_rules
|
&self.rename_all_rules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rename_all_fields_rules(&self) -> &RenameAllRules {
|
||||||
|
&self.rename_all_fields_rules
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transparent(&self) -> bool {
|
pub fn transparent(&self) -> bool {
|
||||||
self.transparent
|
self.transparent
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,14 @@ impl RenameRule {
|
|||||||
ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"),
|
ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the `RenameRule` if it is not `None`, `rule_b` otherwise.
|
||||||
|
pub fn or(&self, rule_b: &Self) -> Self {
|
||||||
|
match self {
|
||||||
|
None => *rule_b,
|
||||||
|
_ => *self,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParseError<'a> {
|
pub struct ParseError<'a> {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub const OTHER: Symbol = Symbol("other");
|
|||||||
pub const REMOTE: Symbol = Symbol("remote");
|
pub const REMOTE: Symbol = Symbol("remote");
|
||||||
pub const RENAME: Symbol = Symbol("rename");
|
pub const RENAME: Symbol = Symbol("rename");
|
||||||
pub const RENAME_ALL: Symbol = Symbol("rename_all");
|
pub const RENAME_ALL: Symbol = Symbol("rename_all");
|
||||||
|
pub const RENAME_ALL_FIELDS: Symbol = Symbol("rename_all_fields");
|
||||||
pub const REPR: Symbol = Symbol("repr");
|
pub const REPR: Symbol = Symbol("repr");
|
||||||
pub const SERDE: Symbol = Symbol("serde");
|
pub const SERDE: Symbol = Symbol("serde");
|
||||||
pub const SERIALIZE: Symbol = Symbol("serialize");
|
pub const SERIALIZE: Symbol = Symbol("serialize");
|
||||||
|
|||||||
@@ -1924,6 +1924,62 @@ fn test_rename_all() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rename_all_fields() {
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
|
#[serde(rename_all_fields = "kebab-case")]
|
||||||
|
enum E {
|
||||||
|
V1,
|
||||||
|
V2(bool),
|
||||||
|
V3 {
|
||||||
|
a_field: bool,
|
||||||
|
another_field: bool,
|
||||||
|
#[serde(rename = "last-field")]
|
||||||
|
yet_another_field: bool,
|
||||||
|
},
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
V4 {
|
||||||
|
a_field: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&E::V3 {
|
||||||
|
a_field: true,
|
||||||
|
another_field: true,
|
||||||
|
yet_another_field: true,
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::StructVariant {
|
||||||
|
name: "E",
|
||||||
|
variant: "V3",
|
||||||
|
len: 3,
|
||||||
|
},
|
||||||
|
Token::Str("a-field"),
|
||||||
|
Token::Bool(true),
|
||||||
|
Token::Str("another-field"),
|
||||||
|
Token::Bool(true),
|
||||||
|
Token::Str("last-field"),
|
||||||
|
Token::Bool(true),
|
||||||
|
Token::StructVariantEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&E::V4 { a_field: true },
|
||||||
|
&[
|
||||||
|
Token::StructVariant {
|
||||||
|
name: "E",
|
||||||
|
variant: "V4",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("a_field"),
|
||||||
|
Token::Bool(true),
|
||||||
|
Token::StructVariantEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_untagged_newtype_variant_containing_unit_struct_not_map() {
|
fn test_untagged_newtype_variant_containing_unit_struct_not_map() {
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
|||||||
Reference in New Issue
Block a user