diff --git a/serde_codegen_internals/src/attr.rs b/serde_codegen_internals/src/attr.rs index b3a5328d..4b0af171 100644 --- a/serde_codegen_internals/src/attr.rs +++ b/serde_codegen_internals/src/attr.rs @@ -12,6 +12,8 @@ use std::str::FromStr; // user will see errors simultaneously for all bad attributes in the crate // rather than just the first. +pub use case::RenameRule; + struct Attr<'c, T> { cx: &'c Ctxt, name: &'static str, @@ -86,102 +88,6 @@ impl Name { } } -#[derive(Debug, PartialEq)] -pub enum RenameRule { - /// Don't apply a default rename rule. - None, - /// 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 { - pub fn apply_to_variant(&self, variant_name: String) -> Result { - if *self == RenameRule::None { - return Ok(variant_name); - } - let mut chars = variant_name.chars(); - let start = chars.next().unwrap(); - if start.is_lowercase() { - return Err(format!("#[serde(rename_all = \"...\")] expects enum variants to be \ - named in `PascalCase`.")); - } - Ok(self.apply_to_words(chars.fold(vec![start.to_lowercase().collect()], |mut words, c| { - if c.is_uppercase() { - words.push(c.to_lowercase().collect()); - } else { - words.last_mut().unwrap().push(c); - } - words - }))) - } - - pub fn apply_to_field(&self, field_name: String) -> Result { - if *self == RenameRule::None { - return Ok(field_name); - } - if field_name != field_name.to_lowercase() { - return Err(format!("#[serde(rename_all = \"...\")] expects fields to be named in \ - `snake_case`.")); - } - Ok(self.apply_to_words(field_name.split('_').map(|s| s.to_string()).collect())) - } - - fn apply_to_words(&self, lowercased_words: Vec) -> String { - match *self { - RenameRule::PascalCase => Self::capitalising_join(lowercased_words), - RenameRule::CamelCase => { - let mut iter = lowercased_words.into_iter(); - let mut first = iter.next().unwrap(); - first.push_str(Self::capitalising_join(iter.collect()).as_str()); - first - } - RenameRule::SnakeCase => Self::delimiting_join(lowercased_words, "_"), - RenameRule::ScreamingSnakeCase => Self::delimiting_join(lowercased_words, "_").to_uppercase(), - RenameRule::KebabCase => Self::delimiting_join(lowercased_words, "-"), - _ => unreachable!(), - } - .clone() - } - - fn delimiting_join(lowercased_words: Vec, delimiter: &str) -> String { - lowercased_words.join(delimiter) - } - - fn capitalising_join(lowercased_words: Vec) -> String { - lowercased_words.into_iter().map(Self::capitalise).collect() - } - - fn capitalise(lowercased_word: String) -> String { - let mut iter = lowercased_word.chars(); - let mut first: String = iter.next().unwrap().to_uppercase().collect(); - first.push_str(iter.collect::().as_str()); - first - } -} - -impl FromStr for RenameRule { - type Err = String; - - fn from_str(rename_all_str: &str) -> Result { - match rename_all_str { - "PascalCase" => Ok(RenameRule::PascalCase), - "camelCase" => Ok(RenameRule::CamelCase), - "snake_case" => Ok(RenameRule::SnakeCase), - "SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase), - "kebab-case" => Ok(RenameRule::KebabCase), - other => Err(other.to_string()), - } - } -} - /// Represents container (e.g. struct) attribute information #[derive(Debug)] pub struct Item { diff --git a/serde_codegen_internals/src/case.rs b/serde_codegen_internals/src/case.rs new file mode 100644 index 00000000..91044c68 --- /dev/null +++ b/serde_codegen_internals/src/case.rs @@ -0,0 +1,97 @@ +use std::str::FromStr; + +#[derive(Debug, PartialEq)] +pub enum RenameRule { + /// Don't apply a default rename rule. + None, + /// 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 { + pub fn apply_to_variant(&self, variant_name: String) -> Result { + if *self == RenameRule::None { + return Ok(variant_name); + } + let mut chars = variant_name.chars(); + let start = chars.next().unwrap(); + if start.is_lowercase() { + return Err(format!("#[serde(rename_all = \"...\")] expects enum variants to be \ + named in `PascalCase`.")); + } + Ok(self.apply_to_words(chars.fold(vec![start.to_lowercase().collect()], |mut words, c| { + if c.is_uppercase() { + words.push(c.to_lowercase().collect()); + } else { + words.last_mut().unwrap().push(c); + } + words + }))) + } + + pub fn apply_to_field(&self, field_name: String) -> Result { + if *self == RenameRule::None { + return Ok(field_name); + } + if field_name != field_name.to_lowercase() { + return Err(format!("#[serde(rename_all = \"...\")] expects fields to be named in \ + `snake_case`.")); + } + Ok(self.apply_to_words(field_name.split('_').map(|s| s.to_string()).collect())) + } + + fn apply_to_words(&self, lowercased_words: Vec) -> String { + match *self { + RenameRule::PascalCase => Self::capitalising_join(lowercased_words), + RenameRule::CamelCase => { + let mut iter = lowercased_words.into_iter(); + let mut first = iter.next().unwrap(); + first.push_str(Self::capitalising_join(iter.collect()).as_str()); + first + } + RenameRule::SnakeCase => Self::delimiting_join(lowercased_words, "_"), + RenameRule::ScreamingSnakeCase => Self::delimiting_join(lowercased_words, "_").to_uppercase(), + RenameRule::KebabCase => Self::delimiting_join(lowercased_words, "-"), + _ => unreachable!(), + } + .clone() + } + + fn delimiting_join(lowercased_words: Vec, delimiter: &str) -> String { + lowercased_words.join(delimiter) + } + + fn capitalising_join(lowercased_words: Vec) -> String { + lowercased_words.into_iter().map(Self::capitalise).collect() + } + + fn capitalise(lowercased_word: String) -> String { + let mut iter = lowercased_word.chars(); + let mut first: String = iter.next().unwrap().to_uppercase().collect(); + first.push_str(iter.collect::().as_str()); + first + } +} + +impl FromStr for RenameRule { + type Err = String; + + fn from_str(rename_all_str: &str) -> Result { + match rename_all_str { + "PascalCase" => Ok(RenameRule::PascalCase), + "camelCase" => Ok(RenameRule::CamelCase), + "snake_case" => Ok(RenameRule::SnakeCase), + "SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase), + "kebab-case" => Ok(RenameRule::KebabCase), + other => Err(other.to_string()), + } + } +} diff --git a/serde_codegen_internals/src/lib.rs b/serde_codegen_internals/src/lib.rs index cd998deb..c5e8885c 100644 --- a/serde_codegen_internals/src/lib.rs +++ b/serde_codegen_internals/src/lib.rs @@ -5,3 +5,5 @@ pub mod attr; mod ctxt; pub use ctxt::Ctxt; + +mod case;