mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-06-15 13:11:01 +00:00
Simplify case conversion implementation
This commit is contained in:
@@ -49,15 +49,15 @@ impl<'a> Item<'a> {
|
|||||||
match body {
|
match body {
|
||||||
Body::Enum(ref mut variants) => {
|
Body::Enum(ref mut variants) => {
|
||||||
for ref mut variant in variants {
|
for ref mut variant in variants {
|
||||||
variant.attrs.rename_by_rule(attrs.rename_all()).unwrap_or_else(|err| cx.error(err));
|
variant.attrs.rename_by_rule(attrs.rename_all());
|
||||||
for ref mut field in &mut variant.fields {
|
for ref mut field in &mut variant.fields {
|
||||||
field.attrs.rename_by_rule(variant.attrs.rename_all()).unwrap_or_else(|err| cx.error(err));
|
field.attrs.rename_by_rule(variant.attrs.rename_all());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Body::Struct(_, ref mut fields) => {
|
Body::Struct(_, ref mut fields) => {
|
||||||
for field in fields {
|
for field in fields {
|
||||||
field.attrs.rename_by_rule(attrs.rename_all()).unwrap_or_else(|err| cx.error(err));
|
field.attrs.rename_by_rule(attrs.rename_all());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -464,14 +464,13 @@ impl Variant {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename_by_rule(&mut self, rename_rule: &RenameRule) -> Result<(), String> {
|
pub fn rename_by_rule(&mut self, rule: &RenameRule) {
|
||||||
if !self.ser_renamed {
|
if !self.ser_renamed {
|
||||||
self.name.serialize = rename_rule.apply_to_variant(self.name.serialize.clone())?;
|
self.name.serialize = rule.apply_to_variant(&self.name.serialize);
|
||||||
}
|
}
|
||||||
if !self.de_renamed {
|
if !self.de_renamed {
|
||||||
self.name.deserialize = rename_rule.apply_to_variant(self.name.deserialize.clone())?;
|
self.name.deserialize = rule.apply_to_variant(&self.name.deserialize);
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename_all(&self) -> &RenameRule {
|
pub fn rename_all(&self) -> &RenameRule {
|
||||||
@@ -667,14 +666,13 @@ impl Field {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename_by_rule(&mut self, rename_rule: &RenameRule) -> Result<(), String> {
|
pub fn rename_by_rule(&mut self, rule: &RenameRule) {
|
||||||
if !self.ser_renamed {
|
if !self.ser_renamed {
|
||||||
self.name.serialize = rename_rule.apply_to_field(self.name.serialize.clone())?;
|
self.name.serialize = rule.apply_to_field(&self.name.serialize);
|
||||||
}
|
}
|
||||||
if !self.de_renamed {
|
if !self.de_renamed {
|
||||||
self.name.deserialize = rename_rule.apply_to_field(self.name.deserialize.clone())?;
|
self.name.deserialize = rule.apply_to_field(&self.name.deserialize);
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_serializing(&self) -> bool {
|
pub fn skip_serializing(&self) -> bool {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
use std::ascii::AsciiExt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use self::RenameRule::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum RenameRule {
|
pub enum RenameRule {
|
||||||
/// Don't apply a default rename rule.
|
/// Don't apply a default rename rule.
|
||||||
@@ -17,67 +20,50 @@ pub enum RenameRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RenameRule {
|
impl RenameRule {
|
||||||
pub fn apply_to_variant(&self, variant_name: String) -> Result<String, String> {
|
pub fn apply_to_variant(&self, variant: &str) -> String {
|
||||||
if *self == RenameRule::None {
|
|
||||||
return Ok(variant_name);
|
|
||||||
}
|
|
||||||
let mut chars = variant_name.chars();
|
|
||||||
let start = chars.next().unwrap();
|
|
||||||
if start.is_lowercase() {
|
|
||||||
return Err(format!("#[serde(rename_all = \"...\")] expects enum variants to be \
|
|
||||||
named in `PascalCase`."));
|
|
||||||
}
|
|
||||||
Ok(self.apply_to_words(chars.fold(vec![start.to_lowercase().collect()], |mut words, c| {
|
|
||||||
if c.is_uppercase() {
|
|
||||||
words.push(c.to_lowercase().collect());
|
|
||||||
} else {
|
|
||||||
words.last_mut().unwrap().push(c);
|
|
||||||
}
|
|
||||||
words
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_to_field(&self, field_name: String) -> Result<String, String> {
|
|
||||||
if *self == RenameRule::None {
|
|
||||||
return Ok(field_name);
|
|
||||||
}
|
|
||||||
if field_name != field_name.to_lowercase() {
|
|
||||||
return Err(format!("#[serde(rename_all = \"...\")] expects fields to be named in \
|
|
||||||
`snake_case`."));
|
|
||||||
}
|
|
||||||
Ok(self.apply_to_words(field_name.split('_').map(|s| s.to_string()).collect()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_to_words(&self, lowercased_words: Vec<String>) -> String {
|
|
||||||
match *self {
|
match *self {
|
||||||
RenameRule::PascalCase => Self::capitalising_join(lowercased_words),
|
None | PascalCase => variant.to_owned(),
|
||||||
RenameRule::CamelCase => {
|
CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
|
||||||
let mut iter = lowercased_words.into_iter();
|
SnakeCase => {
|
||||||
let mut first = iter.next().unwrap();
|
let mut snake = String::new();
|
||||||
first.push_str(Self::capitalising_join(iter.collect()).as_str());
|
for (i, ch) in variant.char_indices() {
|
||||||
first
|
if i > 0 && ch.is_uppercase() {
|
||||||
|
snake.push('_');
|
||||||
|
}
|
||||||
|
snake.push(ch.to_ascii_lowercase());
|
||||||
}
|
}
|
||||||
RenameRule::SnakeCase => Self::delimiting_join(lowercased_words, "_"),
|
snake
|
||||||
RenameRule::ScreamingSnakeCase => Self::delimiting_join(lowercased_words, "_").to_uppercase(),
|
|
||||||
RenameRule::KebabCase => Self::delimiting_join(lowercased_words, "-"),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
}
|
||||||
.clone()
|
ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
|
||||||
|
KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delimiting_join(lowercased_words: Vec<String>, delimiter: &str) -> String {
|
pub fn apply_to_field(&self, field: &str) -> String {
|
||||||
lowercased_words.join(delimiter)
|
match *self {
|
||||||
}
|
None | SnakeCase => field.to_owned(),
|
||||||
|
PascalCase => {
|
||||||
fn capitalising_join(lowercased_words: Vec<String>) -> String {
|
let mut pascal = String::new();
|
||||||
lowercased_words.into_iter().map(Self::capitalise).collect()
|
let mut capitalize = true;
|
||||||
}
|
for ch in field.chars() {
|
||||||
|
if ch == '_' {
|
||||||
fn capitalise(lowercased_word: String) -> String {
|
capitalize = true;
|
||||||
let mut iter = lowercased_word.chars();
|
} else if capitalize {
|
||||||
let mut first: String = iter.next().unwrap().to_uppercase().collect();
|
pascal.push(ch.to_ascii_uppercase());
|
||||||
first.push_str(iter.collect::<String>().as_str());
|
capitalize = false;
|
||||||
first
|
} else {
|
||||||
|
pascal.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pascal
|
||||||
|
}
|
||||||
|
CamelCase => {
|
||||||
|
let pascal = PascalCase.apply_to_field(field);
|
||||||
|
pascal[..1].to_ascii_lowercase() + &pascal[1..]
|
||||||
|
}
|
||||||
|
ScreamingSnakeCase => field.to_ascii_uppercase(),
|
||||||
|
KebabCase => field.replace('_', "-"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,33 +72,44 @@ impl FromStr for RenameRule {
|
|||||||
|
|
||||||
fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
|
fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
|
||||||
match rename_all_str {
|
match rename_all_str {
|
||||||
"PascalCase" => Ok(RenameRule::PascalCase),
|
"PascalCase" => Ok(PascalCase),
|
||||||
"camelCase" => Ok(RenameRule::CamelCase),
|
"camelCase" => Ok(CamelCase),
|
||||||
"snake_case" => Ok(RenameRule::SnakeCase),
|
"snake_case" => Ok(SnakeCase),
|
||||||
"SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase),
|
"SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
|
||||||
"kebab-case" => Ok(RenameRule::KebabCase),
|
"kebab-case" => Ok(KebabCase),
|
||||||
other => Err(other.to_string()),
|
other => Err(other.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rename_rule_variant_strs() {
|
fn rename_variants() {
|
||||||
let variants = vec!["Outcome", "VeryTastyVegetables", "A", "Z42", "bad_snake_case"];
|
for &(original, camel, snake, screaming, kebab) in
|
||||||
let variants_renamed_expected = vec![
|
&[("Outcome", "outcome", "outcome", "OUTCOME", "outcome"),
|
||||||
(PascalCase, vec![Ok("Outcome"), Ok("VeryTastyVegetables"), Ok("A"), Ok("Z42"), Err(())]),
|
("VeryTasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty"),
|
||||||
(CamelCase, vec![Ok("outcome"), Ok("veryTastyVegetables"), Ok("a"), Ok("z42"), Err(())]),
|
("A", "a", "a", "A", "a"),
|
||||||
(SnakeCase, vec![Ok("outcome"), Ok("very_tasty_vegetables"), Ok("a"), Ok("z42"), Err(())]),
|
("Z42", "z42", "z42", "Z42", "z42")] {
|
||||||
(ScreamingSnakeCase, vec![Ok("OUTCOME"), Ok("VERY_TASTY_VEGETABLES"), Ok("A"), Ok("Z42"), Err(())]),
|
assert_eq!(None.apply_to_variant(original), original);
|
||||||
(KebabCase, vec![Ok("outcome"), Ok("very-tasty-vegetables"), Ok("a"), Ok("z42"), Err(())]),
|
assert_eq!(PascalCase.apply_to_variant(original), original);
|
||||||
];
|
assert_eq!(CamelCase.apply_to_variant(original), camel);
|
||||||
|
assert_eq!(SnakeCase.apply_to_variant(original), snake);
|
||||||
for variant in variants.iter() {
|
assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
|
||||||
assert_eq!(RenameRule::None.apply_to_variant(variant.to_string()), Ok(variant.to_string()));
|
assert_eq!(KebabCase.apply_to_variant(original), kebab);
|
||||||
}
|
}
|
||||||
for (rule, expected) in variants_renamed_expected.into_iter().map(|(rule, expected)| (rule, expected.into_iter().map(|expect| expect.map(|str| str.to_string())))) {
|
}
|
||||||
for (variant, expected) in variants.iter().zip(expected) {
|
|
||||||
assert_eq!(rule.apply_to_variant(variant.to_string()).map_err(|_| ()), expected);
|
#[test]
|
||||||
}
|
fn rename_fields() {
|
||||||
|
for &(original, pascal, camel, screaming, kebab) in
|
||||||
|
&[("outcome", "Outcome", "outcome", "OUTCOME", "outcome"),
|
||||||
|
("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty"),
|
||||||
|
("a", "A", "a", "A", "a"),
|
||||||
|
("z42", "Z42", "z42", "Z42", "z42")] {
|
||||||
|
assert_eq!(None.apply_to_field(original), original);
|
||||||
|
assert_eq!(PascalCase.apply_to_field(original), pascal);
|
||||||
|
assert_eq!(CamelCase.apply_to_field(original), camel);
|
||||||
|
assert_eq!(SnakeCase.apply_to_field(original), original);
|
||||||
|
assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
|
||||||
|
assert_eq!(KebabCase.apply_to_field(original), kebab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user