use syn::{Ident, LitStr};
use crate::attr_name;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Case {
Camel,
}
impl Case {
pub(crate) fn parse(lit: &LitStr) -> syn::Result<Case> {
if lit.value() == attr_name::CAMEL_CASE {
Ok(Case::Camel)
} else {
Err(syn::Error::new_spanned(
lit,
"rename_all = \"camelCase\" is the one case",
))
}
}
}
pub(crate) fn field_key(ident: &Ident, case: Option<Case>) -> String {
let name = ident.to_string().trim_start_matches("r#").to_string();
match case {
None => name,
Some(Case::Camel) => camel(&name),
}
}
pub(crate) fn variant_key(ident: &Ident, case: Option<Case>) -> String {
let name = ident.to_string();
match case {
None => snake(&name),
Some(Case::Camel) => camel(&name),
}
}
fn words(name: &str) -> Vec<String> {
let mut out = Vec::new();
for part in name.split('_').filter(|p| !p.is_empty()) {
let mut start = 0;
for (i, c) in part.char_indices().skip(1) {
if c.is_uppercase() {
out.push(part[start..i].to_string());
start = i;
}
}
out.push(part[start..].to_string());
}
out
}
fn camel(name: &str) -> String {
let mut out = String::new();
for (i, w) in words(name).iter().enumerate() {
let mut chars = w.chars();
if let Some(first) = chars.next() {
if i == 0 {
out.extend(first.to_lowercase());
} else {
out.extend(first.to_uppercase());
}
out.push_str(chars.as_str());
}
}
out
}
fn snake(name: &str) -> String {
words(name)
.iter()
.map(|w| w.to_lowercase())
.collect::<Vec<_>>()
.join("_")
}