Replaced case conversion with heck

This commit is contained in:
jaydenelliott 2023-03-27 18:45:05 +11:00
parent bc8ad8aee6
commit d509b3bc52
4 changed files with 60 additions and 111 deletions

View File

@ -15,3 +15,4 @@ test = false
syn = "2.0" syn = "2.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
heck = "0.4"

View File

@ -1,6 +1,11 @@
#[allow(deprecated, unused_imports)] #[allow(deprecated, unused_imports)]
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use heck::{
ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTrainCase,
ToUpperCamelCase,
};
use self::RenameRule::*; use self::RenameRule::*;
/// The different possible ways to change case of fields in a struct, or variants in an enum. /// The different possible ways to change case of fields in a struct, or variants in an enum.
@ -26,78 +31,56 @@ pub enum RenameRule {
KebabCase, KebabCase,
/// Rename direct children to "SCREAMING-KEBAB-CASE" style. /// Rename direct children to "SCREAMING-KEBAB-CASE" style.
ScreamingKebabCase, ScreamingKebabCase,
/// Rename direct children to "Train-Case" style.
TrainCase,
} }
pub static RENAME_RULES: &[(&str, RenameRule)] = &[ pub const RENAME_RULES: &[&str] = &[
("lowercase", LowerCase), "lowercase",
("UPPERCASE", UpperCase), "UPPERCASE",
("PascalCase", PascalCase), "PascalCase",
("camelCase", CamelCase), "camelCase",
("snake_case", SnakeCase), "snake_case",
("SCREAMING_SNAKE_CASE", ScreamingSnakeCase), "SCREAMING_SNAKE_CASE",
("kebab-case", KebabCase), "kebab-case",
("SCREAMING-KEBAB-CASE", ScreamingKebabCase), "SCREAMING-KEBAB-CASE",
"Train-Case",
]; ];
impl RenameRule { impl RenameRule {
/// Apply a renaming rule to an enum variant, returning the version expected in the source. pub fn from_str(rule: &str) -> Option<RenameRule> {
pub fn apply_to_variant(&self, variant: &str) -> String { match rule {
match *self { "lowercase" => Some(LowerCase),
PascalCase => variant.to_owned(), "UPPERCASE" => Some(UpperCase),
LowerCase => variant.to_ascii_lowercase(), "PascalCase" => Some(PascalCase),
UpperCase => variant.to_ascii_uppercase(), "camelCase" => Some(CamelCase),
CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..], "snake_case" => Some(SnakeCase),
SnakeCase => { "SCREAMING_SNAKE_CASE" => Some(ScreamingSnakeCase),
let mut snake = String::new(); "kebab-case" => Some(KebabCase),
for (i, ch) in variant.char_indices() { "SCREAMING-KEBAB-CASE" => Some(ScreamingKebabCase),
if i > 0 && ch.is_uppercase() { "Train-Case" => Some(TrainCase),
snake.push('_'); _ => None,
}
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 an enum or struct field, returning the version expected in the source.
/// Apply a renaming rule to a struct field, returning the version expected in the source. pub fn apply_to_field(&self, variant: &str) -> String {
pub fn apply_to_field(&self, field: &str) -> String {
match *self { match *self {
LowerCase | SnakeCase => field.to_owned(), LowerCase => variant.to_lowercase(),
UpperCase => field.to_ascii_uppercase(), UpperCase => variant.to_uppercase(),
PascalCase => { PascalCase => variant.to_upper_camel_case(),
let mut pascal = String::new(); CamelCase => variant.to_lower_camel_case(),
let mut capitalize = true; SnakeCase => variant.to_snake_case(),
for ch in field.chars() { ScreamingSnakeCase => variant.to_shouty_snake_case(),
if ch == '_' { KebabCase => variant.to_kebab_case(),
capitalize = true; ScreamingKebabCase => variant.to_shouty_kebab_case(),
} else if capitalize { TrainCase => variant.to_train_case(),
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('_', "-"),
} }
} }
} }
#[test] #[test]
fn rename_variants() { fn rename_field() {
for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[ for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[
( (
"Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME",
@ -115,42 +98,11 @@ fn rename_variants() {
("A", "a", "A", "a", "a", "A", "a", "A"), ("A", "a", "A", "a", "a", "A", "a", "A"),
("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"), ("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"),
] { ] {
assert_eq!(LowerCase.apply_to_variant(original), lower); assert_eq!(LowerCase.apply_to_field(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 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!(UpperCase.apply_to_field(original), upper);
assert_eq!(PascalCase.apply_to_field(original), pascal); assert_eq!(PascalCase.apply_to_field(original), original);
assert_eq!(CamelCase.apply_to_field(original), camel); assert_eq!(CamelCase.apply_to_field(original), camel);
assert_eq!(SnakeCase.apply_to_field(original), original); assert_eq!(SnakeCase.apply_to_field(original), snake);
assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming); assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
assert_eq!(KebabCase.apply_to_field(original), kebab); assert_eq!(KebabCase.apply_to_field(original), kebab);
assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab); assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);

View File

@ -22,7 +22,7 @@ impl Variant {
// variant level name override takes precendence over container level rename_all override // variant level name override takes precendence over container level rename_all override
let name = overrides.name.unwrap_or_else(|| match rename_all { let name = overrides.name.unwrap_or_else(|| match rename_all {
Some(rule) => rule.apply_to_variant(&raw.ident.to_string()), Some(rule) => rule.apply_to_field(&raw.ident.to_string()),
None => raw.ident.to_string(), None => raw.ident.to_string(),
}); });
Ok(Variant { Ok(Variant {

View File

@ -56,23 +56,19 @@ impl Overrides {
if name_override { if name_override {
overrides.name = Some(value); overrides.name = Some(value);
} else if rename_all_override { } else if rename_all_override {
let rename_rule = RENAME_RULES let rename_rule = RenameRule::from_str(&value).ok_or_else(|| {
.iter() Error::new_spanned(
.find(|rule| rule.0 == value) &meta.value,
.map(|val| val.1) format!(
.ok_or_else(|| { "invalid rename_all rule, expected one of: {}",
Error::new_spanned( RENAME_RULES
&meta.value, .iter()
format!( .map(|rule| format!("\"{}\"", rule))
"invalid rename_all rule, expected one of: {}", .collect::<Vec<_>>()
RENAME_RULES .join(", ")
.iter() ),
.map(|rule| format!("\"{}\"", rule.0)) )
.collect::<Vec<_>>() })?;
.join(", ")
),
)
})?;
overrides.rename_all = Some(rename_rule); overrides.rename_all = Some(rename_rule);
} }