Merge pull request #1695 from jplatte/rename_all_fields
Add #[serde(rename_all_fields = "foo")] attribute
This commit is contained in:
commit
e74925bc43
@ -86,9 +86,12 @@ impl<'a> Container<'a> {
|
||||
if field.attrs.flatten() {
|
||||
has_flatten = true;
|
||||
}
|
||||
field
|
||||
.attrs
|
||||
.rename_by_rules(variant.attrs.rename_all_rules());
|
||||
field.attrs.rename_by_rules(
|
||||
variant
|
||||
.attrs
|
||||
.rename_all_rules()
|
||||
.or(attrs.rename_all_fields_rules()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,11 +192,23 @@ impl Name {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RenameAllRules {
|
||||
serialize: 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.
|
||||
pub struct Container {
|
||||
name: Name,
|
||||
@ -204,6 +216,7 @@ pub struct Container {
|
||||
deny_unknown_fields: bool,
|
||||
default: Default,
|
||||
rename_all_rules: RenameAllRules,
|
||||
rename_all_fields_rules: RenameAllRules,
|
||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||
tag: TagType,
|
||||
@ -287,6 +300,8 @@ impl Container {
|
||||
let mut default = Attr::none(cx, DEFAULT);
|
||||
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_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 de_bound = Attr::none(cx, BOUND);
|
||||
let mut untagged = BoolAttr::none(cx, UNTAGGED);
|
||||
@ -340,6 +355,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 {
|
||||
// #[serde(transparent)]
|
||||
transparent.set_true(meta.path);
|
||||
@ -527,6 +580,10 @@ impl Container {
|
||||
serialize: rename_all_ser_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(),
|
||||
de_bound: de_bound.get(),
|
||||
tag: decide_tag(cx, item, untagged, internal_tag, content),
|
||||
@ -546,8 +603,12 @@ impl Container {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn rename_all_rules(&self) -> &RenameAllRules {
|
||||
&self.rename_all_rules
|
||||
pub fn rename_all_rules(&self) -> RenameAllRules {
|
||||
self.rename_all_rules
|
||||
}
|
||||
|
||||
pub fn rename_all_fields_rules(&self) -> RenameAllRules {
|
||||
self.rename_all_fields_rules
|
||||
}
|
||||
|
||||
pub fn transparent(&self) -> bool {
|
||||
@ -920,7 +981,7 @@ impl Variant {
|
||||
self.name.deserialize_aliases()
|
||||
}
|
||||
|
||||
pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
|
||||
pub fn rename_by_rules(&mut self, rules: RenameAllRules) {
|
||||
if !self.name.serialize_renamed {
|
||||
self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize);
|
||||
}
|
||||
@ -929,8 +990,8 @@ impl Variant {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename_all_rules(&self) -> &RenameAllRules {
|
||||
&self.rename_all_rules
|
||||
pub fn rename_all_rules(&self) -> RenameAllRules {
|
||||
self.rename_all_rules
|
||||
}
|
||||
|
||||
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||
@ -1259,7 +1320,7 @@ impl Field {
|
||||
self.name.deserialize_aliases()
|
||||
}
|
||||
|
||||
pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
|
||||
pub fn rename_by_rules(&mut self, rules: RenameAllRules) {
|
||||
if !self.name.serialize_renamed {
|
||||
self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize);
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ impl RenameRule {
|
||||
}
|
||||
|
||||
/// Apply a renaming rule to an enum variant, returning the version expected in the source.
|
||||
pub fn apply_to_variant(&self, variant: &str) -> String {
|
||||
match *self {
|
||||
pub fn apply_to_variant(self, variant: &str) -> String {
|
||||
match self {
|
||||
None | PascalCase => variant.to_owned(),
|
||||
LowerCase => variant.to_ascii_lowercase(),
|
||||
UpperCase => variant.to_ascii_uppercase(),
|
||||
@ -79,8 +79,8 @@ impl RenameRule {
|
||||
}
|
||||
|
||||
/// Apply a renaming rule to a struct field, returning the version expected in the source.
|
||||
pub fn apply_to_field(&self, field: &str) -> String {
|
||||
match *self {
|
||||
pub fn apply_to_field(self, field: &str) -> String {
|
||||
match self {
|
||||
None | LowerCase | SnakeCase => field.to_owned(),
|
||||
UpperCase => field.to_ascii_uppercase(),
|
||||
PascalCase => {
|
||||
@ -107,6 +107,14 @@ impl RenameRule {
|
||||
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> {
|
||||
|
@ -23,6 +23,7 @@ pub const OTHER: Symbol = Symbol("other");
|
||||
pub const REMOTE: Symbol = Symbol("remote");
|
||||
pub const RENAME: Symbol = Symbol("rename");
|
||||
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 SERDE: Symbol = Symbol("serde");
|
||||
pub const SERIALIZE: Symbol = Symbol("serialize");
|
||||
|
@ -1923,6 +1923,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]
|
||||
fn test_untagged_newtype_variant_containing_unit_struct_not_map() {
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user