| //! Code to convert the Rust-styled field/variant (e.g. `my_field`, `MyType`) to the |
| //! case of the source (e.g. `my-field`, `MY_FIELD`). |
| |
| // See https://users.rust-lang.org/t/psa-dealing-with-warning-unused-import-std-ascii-asciiext-in-today-s-nightly/13726 |
| #[allow(deprecated, unused_imports)] |
| use std::ascii::AsciiExt; |
| |
| use std::fmt::{self, Debug, Display}; |
| |
| use self::RenameRule::*; |
| |
| /// The different possible ways to change case of fields in a struct, or variants in an enum. |
| #[derive(Copy, Clone, PartialEq)] |
| pub enum RenameRule { |
| /// Don't apply a default rename rule. |
| None, |
| /// 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, |
| } |
| |
| static RENAME_RULES: &[(&str, RenameRule)] = &[ |
| ("lowercase", LowerCase), |
| ("UPPERCASE", UpperCase), |
| ("PascalCase", PascalCase), |
| ("camelCase", CamelCase), |
| ("snake_case", SnakeCase), |
| ("SCREAMING_SNAKE_CASE", ScreamingSnakeCase), |
| ("kebab-case", KebabCase), |
| ("SCREAMING-KEBAB-CASE", ScreamingKebabCase), |
| ]; |
| |
| impl RenameRule { |
| pub fn from_str(rename_all_str: &str) -> Result<Self, ParseError> { |
| for (name, rule) in RENAME_RULES { |
| if rename_all_str == *name { |
| return Ok(*rule); |
| } |
| } |
| Err(ParseError { |
| unknown: rename_all_str, |
| }) |
| } |
| |
| /// 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 { |
| None | 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 { |
| None | 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('_', "-"), |
| } |
| } |
| } |
| |
| pub struct ParseError<'a> { |
| unknown: &'a str, |
| } |
| |
| impl<'a> Display for ParseError<'a> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| f.write_str("unknown rename rule `rename_all = ")?; |
| Debug::fmt(self.unknown, f)?; |
| f.write_str("`, expected one of ")?; |
| for (i, (name, _rule)) in RENAME_RULES.iter().enumerate() { |
| if i > 0 { |
| f.write_str(", ")?; |
| } |
| Debug::fmt(name, f)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| #[test] |
| fn 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!(None.apply_to_variant(original), original); |
| 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 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!(None.apply_to_field(original), original); |
| 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); |
| } |
| } |