1use std::borrow::Cow;
4use std::fmt::{Display, Formatter, Result as FmtResult};
5
6use inflector::Inflector;
7
8#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
10pub enum Name {
11 Named(Cow<'static, str>),
13
14 Generated(Cow<'static, str>),
16}
17
18impl Name {
19 #[must_use]
21 pub const fn named(name: &'static str) -> Self {
22 Self::Named(Cow::Borrowed(name))
23 }
24
25 #[must_use]
27 pub const fn generated(name: &'static str) -> Self {
28 Self::Generated(Cow::Borrowed(name))
29 }
30
31 #[must_use]
33 pub fn new_named<T>(name: T) -> Self
34 where
35 T: Into<Cow<'static, str>>,
36 {
37 Self::Named(name.into())
38 }
39
40 #[must_use]
42 pub fn new_generated<T>(name: T) -> Self
43 where
44 T: Into<Cow<'static, str>>,
45 {
46 Self::Generated(name.into())
47 }
48
49 #[must_use]
51 pub fn is_named(&self) -> bool {
52 matches!(self, Self::Named(_))
53 }
54
55 #[must_use]
57 pub fn is_generated(&self) -> bool {
58 matches!(self, Self::Generated(_))
59 }
60
61 #[must_use]
63 pub fn as_str(&self) -> &str {
64 match self {
65 Self::Named(s) => s,
66 Self::Generated(s) => s,
67 }
68 }
69
70 #[must_use]
72 pub fn as_named_str(&self) -> Option<&str> {
73 match self {
74 Self::Named(s) => Some(s),
75 Self::Generated(_) => None,
76 }
77 }
78
79 #[must_use]
81 pub fn to_type_name(&self) -> String {
82 Self::format_type_name(self.as_str())
83 }
84
85 #[must_use]
87 pub fn to_field_name(&self) -> String {
88 Self::format_field_name(self.as_str())
89 }
90
91 #[must_use]
93 pub fn unify(s: &str) -> String {
94 let mut done = true;
95 let s = s.replace(
96 |c: char| {
97 let replace = !c.is_alphanumeric();
98 if c != '_' && !replace {
99 done = false;
100 }
101
102 c != '_' && replace
103 },
104 "_",
105 );
106
107 if done {
108 s
109 } else {
110 s.to_screaming_snake_case().to_pascal_case()
111 }
112 }
113
114 #[must_use]
116 pub fn format_type_name(s: &str) -> String {
117 let name = Name::unify(s);
118
119 if name.starts_with(char::is_numeric) {
120 format!("_{name}")
121 } else {
122 name
123 }
124 }
125
126 #[must_use]
128 pub fn format_field_name(s: &str) -> String {
129 let mut name = Name::unify(s).to_snake_case();
130
131 if let Ok(idx) = KEYWORDS.binary_search_by(|(key, _)| key.cmp(&&*name)) {
132 name = KEYWORDS[idx].1.into();
133 }
134
135 if name.starts_with(char::is_numeric) {
136 name = format!("_{name}");
137 }
138
139 name
140 }
141}
142
143impl AsRef<str> for Name {
144 fn as_ref(&self) -> &str {
145 self.as_str()
146 }
147}
148
149impl Display for Name {
150 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
151 match self {
152 Self::Named(x) => write!(f, "{x}"),
153 Self::Generated(x) => write!(f, "{x}"),
154 }
155 }
156}
157
158impl From<String> for Name {
159 fn from(value: String) -> Self {
160 Self::Named(Cow::Owned(value))
161 }
162}
163
164impl From<&'static str> for Name {
165 fn from(value: &'static str) -> Self {
166 Self::Named(Cow::Borrowed(value))
167 }
168}
169
170const KEYWORDS: &[(&str, &str)] = &[
171 ("abstract", "abstract_"),
172 ("as", "as_"),
173 ("become", "become_"),
174 ("box", "box_"),
175 ("break", "break_"),
176 ("const", "const_"),
177 ("continue", "continue_"),
178 ("crate", "crate_"),
179 ("do", "do_"),
180 ("else", "else_"),
181 ("enum", "enum_"),
182 ("extern", "extern_"),
183 ("false", "false_"),
184 ("final", "final_"),
185 ("fn", "fn_"),
186 ("for", "for_"),
187 ("if", "if_"),
188 ("impl", "impl_"),
189 ("in", "in_"),
190 ("let", "let_"),
191 ("loop", "loop_"),
192 ("macro", "macro_"),
193 ("match", "match_"),
194 ("mod", "mod_"),
195 ("move", "move_"),
196 ("mut", "mut_"),
197 ("override", "override_"),
198 ("priv", "priv_"),
199 ("pub", "pub_"),
200 ("ref", "ref_"),
201 ("return", "return_"),
202 ("self", "self_"),
203 ("Self", "Self_"),
204 ("static", "static_"),
205 ("struct", "struct_"),
206 ("super", "super_"),
207 ("trait", "trait_"),
208 ("true", "true_"),
209 ("try", "try_"),
210 ("type", "type_"),
211 ("typeof", "typeof_"),
212 ("union", "union_"),
213 ("unsafe", "unsafe_"),
214 ("unsized", "unsized_"),
215 ("use", "use_"),
216 ("virtual", "virtual_"),
217 ("where", "where_"),
218 ("while", "while_"),
219 ("yield", "yield_"),
220];
221
222#[cfg(test)]
223mod tests {
224 use super::Name;
225
226 #[test]
227 fn unify() {
228 assert_eq!("_", Name::unify("+"));
229 assert_eq!("FuuBarBaz", Name::unify("Fuu_BAR_BAZ"));
230 assert_eq!("FuuBarBaz", Name::unify("fuu_bar_baz"));
231 assert_eq!("FuuBarBaz", Name::unify("fuu+Bar-BAZ"));
232 }
233}