//! Crate for changing case of Rust identifiers. //! //! # Features //! * Supports `snake_case`, `lowercase`, `camelCase`, //! `PascalCase`, `SCREAMING_SNAKE_CASE`, and `kebab-case` //! * Rename variants, and fields //! //! # Examples //! ```rust //! use ident_case::RenameRule; //! //! assert_eq!("helloWorld", RenameRule::CamelCase.apply_to_field("hello_world")); //! //! assert_eq!("i_love_serde", RenameRule::SnakeCase.apply_to_variant("ILoveSerde")); //! ``` // Copyright 2017 Serde Developers // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::ascii::AsciiExt; use std::str::FromStr; use self::RenameRule::*; /// A casing rule for renaming Rust identifiers. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum RenameRule { /// No-op rename rule. None, /// Rename direct children to "lowercase" style. LowerCase, /// 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, } impl RenameRule { /// Change case of a `PascalCase` variant. pub fn apply_to_variant>(&self, variant: S) -> String { let variant = variant.as_ref(); match *self { None | PascalCase => variant.to_owned(), LowerCase => variant.to_ascii_lowercase(), 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('_', "-"), } } /// Change case of a `snake_case` field. pub fn apply_to_field>(&self, field: S) -> String { let field = field.as_ref(); match *self { None | LowerCase | SnakeCase => field.to_owned(), 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('_', "-"), } } } impl FromStr for RenameRule { type Err = (); fn from_str(rename_all_str: &str) -> Result { match rename_all_str { "lowercase" => Ok(LowerCase), "PascalCase" => Ok(PascalCase), "camelCase" => Ok(CamelCase), "snake_case" => Ok(SnakeCase), "SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase), "kebab-case" => Ok(KebabCase), _ => Err(()), } } } impl Default for RenameRule { fn default() -> Self { RenameRule::None } } #[cfg(test)] mod tests { use super::RenameRule::*; #[test] fn rename_variants() { for &(original, lower, camel, snake, screaming, kebab) in &[ ("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome"), ("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty"), ("A", "a", "a", "a", "A", "a"), ("Z42", "z42", "z42", "z42", "Z42", "z42"), ] { assert_eq!(None.apply_to_variant(original), original); assert_eq!(LowerCase.apply_to_variant(original), lower); 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); } } #[test] fn rename_fields() { for &(original, pascal, camel, screaming, kebab) in &[ ("outcome", "Outcome", "outcome", "OUTCOME", "outcome"), ("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty"), ("_leading_under", "LeadingUnder", "leadingUnder", "_LEADING_UNDER", "-leading-under"), ("double__under", "DoubleUnder", "doubleUnder", "DOUBLE__UNDER", "double--under"), ("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); } } }