-
-
Notifications
You must be signed in to change notification settings - Fork 216
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix extract attribute conflict with serde (#618)
* fix: ToParameters will fail when using rename when deserializing * done * cargo fmt * done * cargo fmt * fix ci * done
- Loading branch information
1 parent
858ad0a
commit 063b292
Showing
20 changed files
with
706 additions
and
437 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
use std::str::FromStr; | ||
|
||
use self::RenameRule::*; | ||
|
||
/// Rename rule for a field. | ||
#[derive(PartialEq, Eq, Copy, Clone, Debug)] | ||
#[non_exhaustive] | ||
pub enum RenameRule { | ||
/// Rename direct children to "lowercase" style. | ||
LowerCase, | ||
/// Rename direct children to "UPPERCASE" style. | ||
UpperCase, | ||
/// Rename direct children to "PascalCase" style, as typically used for | ||
/// enum variants. | ||
PascalCase, | ||
/// Rename direct children to "camelCase" style. | ||
CamelCase, | ||
/// Rename direct children to "snake_case" style, as commonly used for | ||
/// fields. | ||
SnakeCase, | ||
/// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly | ||
/// used for constants. | ||
ScreamingSnakeCase, | ||
/// Rename direct children to "kebab-case" style. | ||
KebabCase, | ||
/// Rename direct children to "SCREAMING-KEBAB-CASE" style. | ||
ScreamingKebabCase, | ||
} | ||
|
||
impl FromStr for RenameRule { | ||
type Err = crate::Error; | ||
|
||
fn from_str(input: &str) -> Result<Self, Self::Err> { | ||
for (name, rule) in RENAME_RULES { | ||
if input == name { | ||
return Ok(rule); | ||
} | ||
} | ||
Err(crate::Error::other(format!("invalid rename rule: {input}"))) | ||
} | ||
} | ||
|
||
const RENAME_RULES: [(&str, RenameRule); 8] = [ | ||
("lowercase", LowerCase), | ||
("UPPERCASE", UpperCase), | ||
("PascalCase", PascalCase), | ||
("camelCase", CamelCase), | ||
("snake_case", SnakeCase), | ||
("SCREAMING_SNAKE_CASE", ScreamingSnakeCase), | ||
("kebab-case", KebabCase), | ||
("SCREAMING-KEBAB-CASE", ScreamingKebabCase), | ||
]; | ||
impl RenameRule { | ||
/// Apply a renaming rule to an enum variant, returning the version expected in the source. | ||
pub fn apply_to_variant(&self, variant: impl AsRef<str>) -> String { | ||
let variant = variant.as_ref(); | ||
match self { | ||
PascalCase => variant.to_owned(), | ||
LowerCase => variant.to_ascii_lowercase(), | ||
UpperCase => variant.to_ascii_uppercase(), | ||
CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..], | ||
SnakeCase => { | ||
let mut snake = String::new(); | ||
for (i, ch) in variant.char_indices() { | ||
if i > 0 && ch.is_uppercase() { | ||
snake.push('_'); | ||
} | ||
snake.push(ch.to_ascii_lowercase()); | ||
} | ||
snake | ||
} | ||
ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(), | ||
KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"), | ||
ScreamingKebabCase => ScreamingSnakeCase.apply_to_variant(variant).replace('_', "-"), | ||
} | ||
} | ||
|
||
/// 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 { | ||
LowerCase | SnakeCase => field.to_owned(), | ||
UpperCase => field.to_ascii_uppercase(), | ||
PascalCase => { | ||
let mut pascal = String::new(); | ||
let mut capitalize = true; | ||
for ch in field.chars() { | ||
if ch == '_' { | ||
capitalize = true; | ||
} else if capitalize { | ||
pascal.push(ch.to_ascii_uppercase()); | ||
capitalize = false; | ||
} 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('_', "-"), | ||
ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_rename_variants() { | ||
for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[ | ||
( | ||
"Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", | ||
), | ||
( | ||
"VeryTasty", | ||
"verytasty", | ||
"VERYTASTY", | ||
"veryTasty", | ||
"very_tasty", | ||
"VERY_TASTY", | ||
"very-tasty", | ||
"VERY-TASTY", | ||
), | ||
("A", "a", "A", "a", "a", "A", "a", "A"), | ||
("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"), | ||
] { | ||
assert_eq!(LowerCase.apply_to_variant(original), lower); | ||
assert_eq!(UpperCase.apply_to_variant(original), upper); | ||
assert_eq!(PascalCase.apply_to_variant(original), original); | ||
assert_eq!(CamelCase.apply_to_variant(original), camel); | ||
assert_eq!(SnakeCase.apply_to_variant(original), snake); | ||
assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming); | ||
assert_eq!(KebabCase.apply_to_variant(original), kebab); | ||
assert_eq!(ScreamingKebabCase.apply_to_variant(original), screaming_kebab); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_rename_fields() { | ||
for &(original, upper, pascal, camel, screaming, kebab, screaming_kebab) in &[ | ||
( | ||
"outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", | ||
), | ||
( | ||
"very_tasty", | ||
"VERY_TASTY", | ||
"VeryTasty", | ||
"veryTasty", | ||
"VERY_TASTY", | ||
"very-tasty", | ||
"VERY-TASTY", | ||
), | ||
("a", "A", "A", "a", "A", "a", "A"), | ||
("z42", "Z42", "Z42", "z42", "Z42", "z42", "Z42"), | ||
] { | ||
assert_eq!(UpperCase.apply_to_field(original), upper); | ||
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); | ||
assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab); | ||
} | ||
} | ||
} |
Oops, something went wrong.