use std::borrow::Cow;
use std::fmt::{Display, Formatter, Result as FmtResult};
use inflector::Inflector;
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Name {
Named(Cow<'static, str>),
Generated(Cow<'static, str>),
}
impl Name {
#[must_use]
pub const fn named(name: &'static str) -> Self {
Self::Named(Cow::Borrowed(name))
}
#[must_use]
pub const fn generated(name: &'static str) -> Self {
Self::Generated(Cow::Borrowed(name))
}
#[must_use]
pub fn new_named<T>(name: T) -> Self
where
T: Into<Cow<'static, str>>,
{
Self::Named(name.into())
}
#[must_use]
pub fn new_generated<T>(name: T) -> Self
where
T: Into<Cow<'static, str>>,
{
Self::Generated(name.into())
}
#[must_use]
pub fn is_named(&self) -> bool {
matches!(self, Self::Named(_))
}
#[must_use]
pub fn is_generated(&self) -> bool {
matches!(self, Self::Generated(_))
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Named(s) => s,
Self::Generated(s) => s,
}
}
#[must_use]
pub fn as_named_str(&self) -> Option<&str> {
match self {
Self::Named(s) => Some(s),
Self::Generated(_) => None,
}
}
#[must_use]
pub fn to_type_name(&self) -> String {
Self::format_type_name(self.as_str())
}
#[must_use]
pub fn to_field_name(&self) -> String {
Self::format_field_name(self.as_str())
}
#[must_use]
pub fn unify(s: &str) -> String {
let mut done = true;
let s = s.replace(
|c: char| {
let replace = !c.is_alphanumeric();
if c != '_' && !replace {
done = false;
}
c != '_' && replace
},
"_",
);
if done {
s
} else {
s.to_screaming_snake_case().to_pascal_case()
}
}
#[must_use]
pub fn format_type_name(s: &str) -> String {
let name = Name::unify(s);
if name.starts_with(char::is_numeric) {
format!("_{name}")
} else {
name
}
}
#[must_use]
pub fn format_field_name(s: &str) -> String {
let mut name = Name::unify(s).to_snake_case();
if let Ok(idx) = KEYWORDS.binary_search_by(|(key, _)| key.cmp(&&*name)) {
name = KEYWORDS[idx].1.into();
}
if name.starts_with(char::is_numeric) {
name = format!("_{name}");
}
name
}
}
impl AsRef<str> for Name {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Display for Name {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Named(x) => write!(f, "{x}"),
Self::Generated(x) => write!(f, "{x}"),
}
}
}
impl From<String> for Name {
fn from(value: String) -> Self {
Self::Named(Cow::Owned(value))
}
}
impl From<&'static str> for Name {
fn from(value: &'static str) -> Self {
Self::Named(Cow::Borrowed(value))
}
}
const KEYWORDS: &[(&str, &str)] = &[
("abstract", "abstract_"),
("as", "as_"),
("become", "become_"),
("box", "box_"),
("break", "break_"),
("const", "const_"),
("continue", "continue_"),
("crate", "crate_"),
("do", "do_"),
("else", "else_"),
("enum", "enum_"),
("extern", "extern_"),
("false", "false_"),
("final", "final_"),
("fn", "fn_"),
("for", "for_"),
("if", "if_"),
("impl", "impl_"),
("in", "in_"),
("let", "let_"),
("loop", "loop_"),
("macro", "macro_"),
("match", "match_"),
("mod", "mod_"),
("move", "move_"),
("mut", "mut_"),
("override", "override_"),
("priv", "priv_"),
("pub", "pub_"),
("ref", "ref_"),
("return", "return_"),
("self", "self_"),
("Self", "Self_"),
("static", "static_"),
("struct", "struct_"),
("super", "super_"),
("trait", "trait_"),
("true", "true_"),
("try", "try_"),
("type", "type_"),
("typeof", "typeof_"),
("union", "union_"),
("unsafe", "unsafe_"),
("unsized", "unsized_"),
("use", "use_"),
("virtual", "virtual_"),
("where", "where_"),
("while", "while_"),
("yield", "yield_"),
];
#[cfg(test)]
mod tests {
use super::Name;
#[test]
fn unify() {
assert_eq!("_", Name::unify("+"));
assert_eq!("FuuBarBaz", Name::unify("Fuu_BAR_BAZ"));
assert_eq!("FuuBarBaz", Name::unify("fuu_bar_baz"));
assert_eq!("FuuBarBaz", Name::unify("fuu+Bar-BAZ"));
}
}