use proc_macro2::Span;
use syn::Ident;
const KEYWORDS: &[&str] = &[
"as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern",
"false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub",
"ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "union",
"unsafe", "use", "where", "while",
];
pub fn sanitize_ident(key: &str) -> String {
if key.is_empty() {
return "_empty".to_owned();
}
let mut out = String::with_capacity(key.len());
let mut chars = key.chars().peekable();
let first = chars.next().unwrap();
if first.is_ascii_digit() {
out.push('_');
out.push(first);
} else if first.is_alphanumeric() || first == '_' {
out.push(first);
} else {
out.push('_');
}
for ch in chars {
if ch.is_alphanumeric() || ch == '_' {
out.push(ch);
} else {
out.push('_');
}
}
if KEYWORDS.contains(&out.as_str()) {
format!("r#{out}")
} else {
out
}
}
pub fn to_screaming_snake(name: &str) -> String {
let mut out = String::new();
for (i, ch) in name.char_indices() {
if ch.is_uppercase() {
if i > 0 {
out.push('_');
}
out.push(ch);
} else {
out.push(ch.to_ascii_uppercase());
}
}
out
}
pub fn to_snake_case(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 4);
let chars: Vec<char> = s.chars().collect();
for (i, &c) in chars.iter().enumerate() {
if c.is_ascii_uppercase() {
if i > 0 {
let prev = chars[i - 1];
let next_lower = chars
.get(i + 1)
.map(|c| c.is_ascii_lowercase())
.unwrap_or(false);
if prev.is_ascii_lowercase() || (prev.is_ascii_uppercase() && next_lower) {
out.push('_');
}
}
out.push(c.to_ascii_lowercase());
} else {
out.push(c);
}
}
out.chars()
.map(|c| {
if c.is_alphanumeric() || c == '_' {
c
} else {
'_'
}
})
.collect()
}
pub fn to_pascal_case(s: &str) -> String {
s.split('_')
.filter(|seg| !seg.is_empty())
.map(|seg| {
let mut chars = seg.chars();
match chars.next() {
None => String::new(),
Some(first) => {
let mut word = String::new();
word.push(first.to_ascii_uppercase());
word.push_str(chars.as_str());
word
},
}
})
.collect()
}
pub fn to_camel_case(s: &str) -> String {
let pascal = to_pascal_case(s);
let mut chars = pascal.chars();
match chars.next() {
None => String::new(),
Some(first) => {
let mut out = String::new();
out.push(first.to_ascii_lowercase());
out.push_str(chars.as_str());
out
},
}
}
pub fn make_ident(name: &str) -> Ident {
if let Some(stripped) = name.strip_prefix("r#") {
Ident::new_raw(stripped, Span::call_site())
} else {
Ident::new(name, Span::call_site())
}
}