17e2e9c0cSopenharmony_ci//! Code to convert the Rust-styled field/variant (e.g. `my_field`, `MyType`) to the 27e2e9c0cSopenharmony_ci//! case of the source (e.g. `my-field`, `MY_FIELD`). 37e2e9c0cSopenharmony_ci 47e2e9c0cSopenharmony_ciuse self::RenameRule::*; 57e2e9c0cSopenharmony_ciuse std::fmt::{self, Debug, Display}; 67e2e9c0cSopenharmony_ci 77e2e9c0cSopenharmony_ci/// The different possible ways to change case of fields in a struct, or variants in an enum. 87e2e9c0cSopenharmony_ci#[derive(Copy, Clone, PartialEq)] 97e2e9c0cSopenharmony_cipub enum RenameRule { 107e2e9c0cSopenharmony_ci /// Don't apply a default rename rule. 117e2e9c0cSopenharmony_ci None, 127e2e9c0cSopenharmony_ci /// Rename direct children to "lowercase" style. 137e2e9c0cSopenharmony_ci LowerCase, 147e2e9c0cSopenharmony_ci /// Rename direct children to "UPPERCASE" style. 157e2e9c0cSopenharmony_ci UpperCase, 167e2e9c0cSopenharmony_ci /// Rename direct children to "PascalCase" style, as typically used for 177e2e9c0cSopenharmony_ci /// enum variants. 187e2e9c0cSopenharmony_ci PascalCase, 197e2e9c0cSopenharmony_ci /// Rename direct children to "camelCase" style. 207e2e9c0cSopenharmony_ci CamelCase, 217e2e9c0cSopenharmony_ci /// Rename direct children to "snake_case" style, as commonly used for 227e2e9c0cSopenharmony_ci /// fields. 237e2e9c0cSopenharmony_ci SnakeCase, 247e2e9c0cSopenharmony_ci /// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly 257e2e9c0cSopenharmony_ci /// used for constants. 267e2e9c0cSopenharmony_ci ScreamingSnakeCase, 277e2e9c0cSopenharmony_ci /// Rename direct children to "kebab-case" style. 287e2e9c0cSopenharmony_ci KebabCase, 297e2e9c0cSopenharmony_ci /// Rename direct children to "SCREAMING-KEBAB-CASE" style. 307e2e9c0cSopenharmony_ci ScreamingKebabCase, 317e2e9c0cSopenharmony_ci} 327e2e9c0cSopenharmony_ci 337e2e9c0cSopenharmony_cistatic RENAME_RULES: &[(&str, RenameRule)] = &[ 347e2e9c0cSopenharmony_ci ("lowercase", LowerCase), 357e2e9c0cSopenharmony_ci ("UPPERCASE", UpperCase), 367e2e9c0cSopenharmony_ci ("PascalCase", PascalCase), 377e2e9c0cSopenharmony_ci ("camelCase", CamelCase), 387e2e9c0cSopenharmony_ci ("snake_case", SnakeCase), 397e2e9c0cSopenharmony_ci ("SCREAMING_SNAKE_CASE", ScreamingSnakeCase), 407e2e9c0cSopenharmony_ci ("kebab-case", KebabCase), 417e2e9c0cSopenharmony_ci ("SCREAMING-KEBAB-CASE", ScreamingKebabCase), 427e2e9c0cSopenharmony_ci]; 437e2e9c0cSopenharmony_ci 447e2e9c0cSopenharmony_ciimpl RenameRule { 457e2e9c0cSopenharmony_ci pub fn from_str(rename_all_str: &str) -> Result<Self, ParseError> { 467e2e9c0cSopenharmony_ci for (name, rule) in RENAME_RULES { 477e2e9c0cSopenharmony_ci if rename_all_str == *name { 487e2e9c0cSopenharmony_ci return Ok(*rule); 497e2e9c0cSopenharmony_ci } 507e2e9c0cSopenharmony_ci } 517e2e9c0cSopenharmony_ci Err(ParseError { 527e2e9c0cSopenharmony_ci unknown: rename_all_str, 537e2e9c0cSopenharmony_ci }) 547e2e9c0cSopenharmony_ci } 557e2e9c0cSopenharmony_ci 567e2e9c0cSopenharmony_ci /// Apply a renaming rule to an enum variant, returning the version expected in the source. 577e2e9c0cSopenharmony_ci pub fn apply_to_variant(self, variant: &str) -> String { 587e2e9c0cSopenharmony_ci match self { 597e2e9c0cSopenharmony_ci None | PascalCase => variant.to_owned(), 607e2e9c0cSopenharmony_ci LowerCase => variant.to_ascii_lowercase(), 617e2e9c0cSopenharmony_ci UpperCase => variant.to_ascii_uppercase(), 627e2e9c0cSopenharmony_ci CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..], 637e2e9c0cSopenharmony_ci SnakeCase => { 647e2e9c0cSopenharmony_ci let mut snake = String::new(); 657e2e9c0cSopenharmony_ci for (i, ch) in variant.char_indices() { 667e2e9c0cSopenharmony_ci if i > 0 && ch.is_uppercase() { 677e2e9c0cSopenharmony_ci snake.push('_'); 687e2e9c0cSopenharmony_ci } 697e2e9c0cSopenharmony_ci snake.push(ch.to_ascii_lowercase()); 707e2e9c0cSopenharmony_ci } 717e2e9c0cSopenharmony_ci snake 727e2e9c0cSopenharmony_ci } 737e2e9c0cSopenharmony_ci ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(), 747e2e9c0cSopenharmony_ci KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"), 757e2e9c0cSopenharmony_ci ScreamingKebabCase => ScreamingSnakeCase 767e2e9c0cSopenharmony_ci .apply_to_variant(variant) 777e2e9c0cSopenharmony_ci .replace('_', "-"), 787e2e9c0cSopenharmony_ci } 797e2e9c0cSopenharmony_ci } 807e2e9c0cSopenharmony_ci 817e2e9c0cSopenharmony_ci /// Apply a renaming rule to a struct field, returning the version expected in the source. 827e2e9c0cSopenharmony_ci pub fn apply_to_field(self, field: &str) -> String { 837e2e9c0cSopenharmony_ci match self { 847e2e9c0cSopenharmony_ci None | LowerCase | SnakeCase => field.to_owned(), 857e2e9c0cSopenharmony_ci UpperCase => field.to_ascii_uppercase(), 867e2e9c0cSopenharmony_ci PascalCase => { 877e2e9c0cSopenharmony_ci let mut pascal = String::new(); 887e2e9c0cSopenharmony_ci let mut capitalize = true; 897e2e9c0cSopenharmony_ci for ch in field.chars() { 907e2e9c0cSopenharmony_ci if ch == '_' { 917e2e9c0cSopenharmony_ci capitalize = true; 927e2e9c0cSopenharmony_ci } else if capitalize { 937e2e9c0cSopenharmony_ci pascal.push(ch.to_ascii_uppercase()); 947e2e9c0cSopenharmony_ci capitalize = false; 957e2e9c0cSopenharmony_ci } else { 967e2e9c0cSopenharmony_ci pascal.push(ch); 977e2e9c0cSopenharmony_ci } 987e2e9c0cSopenharmony_ci } 997e2e9c0cSopenharmony_ci pascal 1007e2e9c0cSopenharmony_ci } 1017e2e9c0cSopenharmony_ci CamelCase => { 1027e2e9c0cSopenharmony_ci let pascal = PascalCase.apply_to_field(field); 1037e2e9c0cSopenharmony_ci pascal[..1].to_ascii_lowercase() + &pascal[1..] 1047e2e9c0cSopenharmony_ci } 1057e2e9c0cSopenharmony_ci ScreamingSnakeCase => field.to_ascii_uppercase(), 1067e2e9c0cSopenharmony_ci KebabCase => field.replace('_', "-"), 1077e2e9c0cSopenharmony_ci ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"), 1087e2e9c0cSopenharmony_ci } 1097e2e9c0cSopenharmony_ci } 1107e2e9c0cSopenharmony_ci 1117e2e9c0cSopenharmony_ci /// Returns the `RenameRule` if it is not `None`, `rule_b` otherwise. 1127e2e9c0cSopenharmony_ci pub fn or(self, rule_b: Self) -> Self { 1137e2e9c0cSopenharmony_ci match self { 1147e2e9c0cSopenharmony_ci None => rule_b, 1157e2e9c0cSopenharmony_ci _ => self, 1167e2e9c0cSopenharmony_ci } 1177e2e9c0cSopenharmony_ci } 1187e2e9c0cSopenharmony_ci} 1197e2e9c0cSopenharmony_ci 1207e2e9c0cSopenharmony_cipub struct ParseError<'a> { 1217e2e9c0cSopenharmony_ci unknown: &'a str, 1227e2e9c0cSopenharmony_ci} 1237e2e9c0cSopenharmony_ci 1247e2e9c0cSopenharmony_ciimpl<'a> Display for ParseError<'a> { 1257e2e9c0cSopenharmony_ci fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 1267e2e9c0cSopenharmony_ci f.write_str("unknown rename rule `rename_all = ")?; 1277e2e9c0cSopenharmony_ci Debug::fmt(self.unknown, f)?; 1287e2e9c0cSopenharmony_ci f.write_str("`, expected one of ")?; 1297e2e9c0cSopenharmony_ci for (i, (name, _rule)) in RENAME_RULES.iter().enumerate() { 1307e2e9c0cSopenharmony_ci if i > 0 { 1317e2e9c0cSopenharmony_ci f.write_str(", ")?; 1327e2e9c0cSopenharmony_ci } 1337e2e9c0cSopenharmony_ci Debug::fmt(name, f)?; 1347e2e9c0cSopenharmony_ci } 1357e2e9c0cSopenharmony_ci Ok(()) 1367e2e9c0cSopenharmony_ci } 1377e2e9c0cSopenharmony_ci} 1387e2e9c0cSopenharmony_ci 1397e2e9c0cSopenharmony_ci#[test] 1407e2e9c0cSopenharmony_cifn rename_variants() { 1417e2e9c0cSopenharmony_ci for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[ 1427e2e9c0cSopenharmony_ci ( 1437e2e9c0cSopenharmony_ci "Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", 1447e2e9c0cSopenharmony_ci ), 1457e2e9c0cSopenharmony_ci ( 1467e2e9c0cSopenharmony_ci "VeryTasty", 1477e2e9c0cSopenharmony_ci "verytasty", 1487e2e9c0cSopenharmony_ci "VERYTASTY", 1497e2e9c0cSopenharmony_ci "veryTasty", 1507e2e9c0cSopenharmony_ci "very_tasty", 1517e2e9c0cSopenharmony_ci "VERY_TASTY", 1527e2e9c0cSopenharmony_ci "very-tasty", 1537e2e9c0cSopenharmony_ci "VERY-TASTY", 1547e2e9c0cSopenharmony_ci ), 1557e2e9c0cSopenharmony_ci ("A", "a", "A", "a", "a", "A", "a", "A"), 1567e2e9c0cSopenharmony_ci ("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"), 1577e2e9c0cSopenharmony_ci ] { 1587e2e9c0cSopenharmony_ci assert_eq!(None.apply_to_variant(original), original); 1597e2e9c0cSopenharmony_ci assert_eq!(LowerCase.apply_to_variant(original), lower); 1607e2e9c0cSopenharmony_ci assert_eq!(UpperCase.apply_to_variant(original), upper); 1617e2e9c0cSopenharmony_ci assert_eq!(PascalCase.apply_to_variant(original), original); 1627e2e9c0cSopenharmony_ci assert_eq!(CamelCase.apply_to_variant(original), camel); 1637e2e9c0cSopenharmony_ci assert_eq!(SnakeCase.apply_to_variant(original), snake); 1647e2e9c0cSopenharmony_ci assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming); 1657e2e9c0cSopenharmony_ci assert_eq!(KebabCase.apply_to_variant(original), kebab); 1667e2e9c0cSopenharmony_ci assert_eq!( 1677e2e9c0cSopenharmony_ci ScreamingKebabCase.apply_to_variant(original), 1687e2e9c0cSopenharmony_ci screaming_kebab 1697e2e9c0cSopenharmony_ci ); 1707e2e9c0cSopenharmony_ci } 1717e2e9c0cSopenharmony_ci} 1727e2e9c0cSopenharmony_ci 1737e2e9c0cSopenharmony_ci#[test] 1747e2e9c0cSopenharmony_cifn rename_fields() { 1757e2e9c0cSopenharmony_ci for &(original, upper, pascal, camel, screaming, kebab, screaming_kebab) in &[ 1767e2e9c0cSopenharmony_ci ( 1777e2e9c0cSopenharmony_ci "outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", 1787e2e9c0cSopenharmony_ci ), 1797e2e9c0cSopenharmony_ci ( 1807e2e9c0cSopenharmony_ci "very_tasty", 1817e2e9c0cSopenharmony_ci "VERY_TASTY", 1827e2e9c0cSopenharmony_ci "VeryTasty", 1837e2e9c0cSopenharmony_ci "veryTasty", 1847e2e9c0cSopenharmony_ci "VERY_TASTY", 1857e2e9c0cSopenharmony_ci "very-tasty", 1867e2e9c0cSopenharmony_ci "VERY-TASTY", 1877e2e9c0cSopenharmony_ci ), 1887e2e9c0cSopenharmony_ci ("a", "A", "A", "a", "A", "a", "A"), 1897e2e9c0cSopenharmony_ci ("z42", "Z42", "Z42", "z42", "Z42", "z42", "Z42"), 1907e2e9c0cSopenharmony_ci ] { 1917e2e9c0cSopenharmony_ci assert_eq!(None.apply_to_field(original), original); 1927e2e9c0cSopenharmony_ci assert_eq!(UpperCase.apply_to_field(original), upper); 1937e2e9c0cSopenharmony_ci assert_eq!(PascalCase.apply_to_field(original), pascal); 1947e2e9c0cSopenharmony_ci assert_eq!(CamelCase.apply_to_field(original), camel); 1957e2e9c0cSopenharmony_ci assert_eq!(SnakeCase.apply_to_field(original), original); 1967e2e9c0cSopenharmony_ci assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming); 1977e2e9c0cSopenharmony_ci assert_eq!(KebabCase.apply_to_field(original), kebab); 1987e2e9c0cSopenharmony_ci assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab); 1997e2e9c0cSopenharmony_ci } 2007e2e9c0cSopenharmony_ci} 201