xsd_parser/types/
name.rs

1//! Contains the [`Name`] helper type and all related types.
2
3use std::borrow::Cow;
4use std::fmt::{Display, Formatter, Result as FmtResult};
5
6use inflector::Inflector;
7
8/// Type that represents a name of a XSD element.
9#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
10pub enum Name {
11    /// The name was explicitly set to the given value.
12    Named(Cow<'static, str>),
13
14    /// The name was generated.
15    Generated(Cow<'static, str>),
16}
17
18impl Name {
19    /// Create a new [`Name::Named`] using the passed `name`.
20    #[must_use]
21    pub const fn named(name: &'static str) -> Self {
22        Self::Named(Cow::Borrowed(name))
23    }
24
25    /// Create a new [`Name::Generated`] using the passed `name`.
26    #[must_use]
27    pub const fn generated(name: &'static str) -> Self {
28        Self::Generated(Cow::Borrowed(name))
29    }
30
31    /// Create a new [`Name::Named`] using the passed `name`.
32    #[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    /// Create a new [`Name::Generated`] using the passed `name`.
41    #[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    /// Returns `true` if this is a [`Name::Named`], `false` otherwise.
50    #[must_use]
51    pub fn is_named(&self) -> bool {
52        matches!(self, Self::Named(_))
53    }
54
55    /// Returns `true` if this is a [`Name::Generated`], `false` otherwise.
56    #[must_use]
57    pub fn is_generated(&self) -> bool {
58        matches!(self, Self::Generated(_))
59    }
60
61    /// Returns the value of [`Name::Named`] or [`Name::Generated`].
62    #[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    /// Returns the value of [`Name::Named`] or `None`.
71    #[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    /// Formats this name as type name.
80    #[must_use]
81    pub fn to_type_name(&self) -> String {
82        Self::format_type_name(self.as_str())
83    }
84
85    /// Formats this name as field name.
86    #[must_use]
87    pub fn to_field_name(&self) -> String {
88        Self::format_field_name(self.as_str())
89    }
90
91    /// Unifies the passed string `s`.
92    #[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    /// Formats the passed string `s` as type name.
115    #[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    /// Formats the passed string `s` as field name.
127    #[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}