use syn::Error;
use syn::LitStr;
use syn::Result;
#[must_use]
pub(crate) enum SerdeRenameRule {
Lowercase,
Uppercase,
PascalCase,
CamelCase,
SnakeCase,
ScreamingSnakeCase,
KebabCase,
ScreamingKebabCase,
}
impl SerdeRenameRule {
pub(crate) fn parse(literal: &LitStr) -> Result<Self> {
match literal.value().as_str() {
"lowercase" => Ok(Self::Lowercase),
"UPPERCASE" => Ok(Self::Uppercase),
"PascalCase" => Ok(Self::PascalCase),
"camelCase" => Ok(Self::CamelCase),
"snake_case" => Ok(Self::SnakeCase),
"SCREAMING_SNAKE_CASE" => Ok(Self::ScreamingSnakeCase),
"kebab-case" => Ok(Self::KebabCase),
"SCREAMING-KEBAB-CASE" => Ok(Self::ScreamingKebabCase),
value => Err(Error::new_spanned(
literal,
format!(
"unsupported serde rename_all rule `{value}`; use a standard serde field \
rename rule",
),
)),
}
}
#[must_use]
pub(crate) fn apply_to_field(&self, name: &str) -> String {
match self {
Self::Lowercase | Self::SnakeCase => name.to_owned(),
Self::Uppercase => name.to_ascii_uppercase(),
Self::PascalCase => pascal_case(name),
Self::CamelCase => {
let mut pascal = pascal_case(name);
if let Some(first) = pascal.get_mut(..1) {
first.make_ascii_lowercase();
}
pascal
}
Self::ScreamingSnakeCase => name.to_ascii_uppercase(),
Self::KebabCase => name.replace('_', "-"),
Self::ScreamingKebabCase => name.to_ascii_uppercase().replace('_', "-"),
}
}
#[must_use]
pub(crate) fn apply_to_variant(&self, name: &str) -> String {
match self {
Self::Lowercase => name.to_ascii_lowercase(),
Self::Uppercase => name.to_ascii_uppercase(),
Self::PascalCase => name.to_owned(),
Self::CamelCase => {
let mut output = name.to_owned();
if let Some(first) = output.get_mut(..1) {
first.make_ascii_lowercase();
}
output
}
Self::SnakeCase => snake_case(name),
Self::ScreamingSnakeCase => snake_case(name).to_ascii_uppercase(),
Self::KebabCase => snake_case(name).replace('_', "-"),
Self::ScreamingKebabCase => snake_case(name).to_ascii_uppercase().replace('_', "-"),
}
}
}
#[must_use]
fn pascal_case(name: &str) -> String {
let mut output = String::new();
let mut capitalize = true;
for character in name.chars() {
if character == '_' {
capitalize = true;
} else if capitalize {
output.push(character.to_ascii_uppercase());
capitalize = false;
} else {
output.push(character);
}
}
output
}
#[must_use]
fn snake_case(name: &str) -> String {
let mut output = String::new();
for (index, character) in name.char_indices() {
if index > 0 && character.is_uppercase() {
output.push('_');
}
output.push(character.to_ascii_lowercase());
}
output
}