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};
5use std::mem::take;
6
7use inflector::Inflector;
8
9/// Type that represents a name of a XSD element
10#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
11pub enum Name {
12    /// The name was explicitly set to the given value.
13    Named(Cow<'static, str>),
14
15    /// The name is unknown and should be generated out of the provided information.
16    Unnamed {
17        /// Unique id for this name.
18        id: usize,
19
20        /// Extension that may be added to the name.
21        ext: Option<Cow<'static, str>>,
22    },
23}
24
25impl Name {
26    /// Generate a unified name from the passed string.
27    #[must_use]
28    pub fn unify(s: &str) -> String {
29        // This is a trick to get weird type name to simple pascal case
30        s.replace('.', "_")
31            .to_screaming_snake_case()
32            .to_pascal_case()
33    }
34
35    /// Create a new [`Name::Named`] using the passed `name`.
36    #[must_use]
37    pub const fn named(name: &'static str) -> Self {
38        Self::Named(Cow::Borrowed(name))
39    }
40
41    /// Create a new [`Name::Named`] using the passed `name`.
42    #[must_use]
43    pub fn new<S: Into<String>>(name: S) -> Self {
44        Self::Named(Cow::Owned(name.into()))
45    }
46
47    /// Remove the provided `suffix` from the [`Name::Named`] or the [`Name::Unnamed::ext`]
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// # use std::borrow::Cow;
53    /// # use xsd_parser::types::Name;
54    /// let name = Name::new("test-fuu");
55    /// let expected = Name::new("test");
56    /// assert_eq!(name.remove_suffix("-fuu"), expected);
57    ///
58    /// let name = Name::Unnamed { id: 123, ext: Some(Cow::Borrowed("test-fuu")) };
59    /// let expected = Name::Unnamed { id: 123, ext: Some(Cow::Owned("test".into())) };;
60    /// assert_eq!(name.remove_suffix("-fuu"), expected);
61    /// ```
62    #[must_use]
63    pub fn remove_suffix(&self, suffix: &str) -> Self {
64        match self {
65            Self::Named(s) => {
66                if let Some(s) = s.strip_suffix(suffix) {
67                    Self::new(s)
68                } else {
69                    Self::Named(s.clone())
70                }
71            }
72            Self::Unnamed { id, ext: Some(ext) } => {
73                if let Some(ext) = ext.strip_suffix(suffix) {
74                    Self::Unnamed {
75                        id: *id,
76                        ext: Some(Cow::Owned(ext.to_owned())),
77                    }
78                } else {
79                    Self::Unnamed {
80                        id: *id,
81                        ext: Some(ext.clone()),
82                    }
83                }
84            }
85            x => x.clone(),
86        }
87    }
88
89    /// Create a type name from this [`Name`] object.
90    ///
91    /// This method can be used to generate a rust type name from this name object.
92    /// The resulting name is written in pascal case and may or may not contain
93    /// additional information depending on the passed arguments.
94    ///
95    /// # Arguments
96    /// - `with_id` Wether to add the unique id of the name to the resulting name or not
97    /// - `name` Optional name that should be added to the resulting name
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// # use std::borrow::Cow;
103    /// # use xsd_parser::types::Name;
104    ///
105    /// let name = Name::new("test");
106    /// assert_eq!(Name::new("Test"), name.to_type_name(false, None));
107    /// assert_eq!(Name::new("Test"), name.to_type_name(true, None));
108    /// assert_eq!(Name::new("Test"), name.to_type_name(false, Some("extra")));
109    /// assert_eq!(Name::new("Test"), name.to_type_name(true, Some("extra")));
110    ///
111    /// let name = Name::Unnamed { id: 123, ext: None };
112    /// assert_eq!(Name::new("Unnamed123"), name.to_type_name(false, None));
113    /// assert_eq!(Name::new("Extra"), name.to_type_name(false, Some("extra")));
114    /// assert_eq!(Name::new("Unnamed123"), name.to_type_name(true, None));
115    /// assert_eq!(Name::new("Extra123"), name.to_type_name(true, Some("extra")));
116    ///
117    /// let name = Name::Unnamed { id: 123, ext: Some(Cow::Borrowed("ext")) };
118    /// assert_eq!(Name::new("ExtUnnamed123"), name.to_type_name(false, None));
119    /// assert_eq!(Name::new("ExtExtra"), name.to_type_name(false, Some("extra")));
120    /// assert_eq!(Name::new("ExtUnnamed123"), name.to_type_name(true, None));
121    /// assert_eq!(Name::new("ExtExtra123"), name.to_type_name(true, Some("extra")));
122    /// ```
123    #[must_use]
124    pub fn to_type_name(&self, with_id: bool, name: Option<&str>) -> Self {
125        match (self, name) {
126            (Self::Named(s), _) => Self::Named(Cow::Owned(s.to_pascal_case())),
127            (Self::Unnamed { id, ext: Some(ext) }, Some(name)) if with_id => Self::Named(
128                Cow::Owned(format!("{}{}{id}", Self::unify(ext), Self::unify(name))),
129            ),
130            (Self::Unnamed { ext: Some(ext), .. }, Some(name)) => Self::Named(Cow::Owned(format!(
131                "{}{}",
132                Self::unify(ext),
133                Self::unify(name)
134            ))),
135            (Self::Unnamed { id, ext: None, .. }, Some(name)) if with_id => {
136                Self::Named(Cow::Owned(format!("{}{id}", Self::unify(name))))
137            }
138            (Self::Unnamed { ext: None, .. }, Some(name)) => {
139                Self::Named(Cow::Owned(Self::unify(name)))
140            }
141            (
142                Self::Unnamed {
143                    id, ext: Some(ext), ..
144                },
145                None,
146            ) => Self::Named(Cow::Owned(format!("{}Unnamed{id}", Self::unify(ext)))),
147            (Self::Unnamed { id, ext: None, .. }, None) => {
148                Self::Named(Cow::Owned(format!("Unnamed{id}")))
149            }
150        }
151    }
152
153    /// Returns `true` if this is a [`Name::Named`], `false` otherwise.
154    #[must_use]
155    pub fn is_named(&self) -> bool {
156        matches!(self, Self::Named(_))
157    }
158
159    /// Returns `true` if this is a [`Name::Unnamed`], `false` otherwise.
160    #[must_use]
161    pub fn is_unnamed(&self) -> bool {
162        matches!(self, Self::Unnamed { .. })
163    }
164
165    /// Returns `true` if this is a [`Name::Unnamed`] with extensions, `false` otherwise.
166    #[must_use]
167    pub fn has_extension(&self) -> bool {
168        matches!(self, Self::Unnamed { ext: Some(_), .. })
169    }
170
171    /// Returns the value of [`Name::Named`] as `Some(&str)`, or `None` if it's an [`Name::Unnamed`].
172    #[must_use]
173    pub fn as_str(&self) -> Option<&str> {
174        match self {
175            Self::Named(s) => Some(s),
176            Self::Unnamed { .. } => None,
177        }
178    }
179
180    /// Adds extensions to this name.
181    ///
182    /// # Arguments
183    /// - `replace` replace any existing extension with the new one
184    /// - `iter` iterator of extensions to apply
185    ///
186    /// # Examples
187    ///
188    /// ```
189    /// # use std::borrow::Cow;
190    /// # use xsd_parser::types::Name;
191    ///
192    /// let name = Name::new("test");
193    /// assert_eq!(Name::new("ExtTest"), name.extend(false, Some("ext")));
194    ///
195    /// let name = Name::Unnamed { id: 123, ext: Some(Cow::Borrowed("ext")) };
196    /// assert_eq!(Name::Unnamed { id: 123, ext: Some(Cow::Owned("Fuu".into())) }, name.clone().extend(true, Some("fuu")));
197    /// assert_eq!(Name::Unnamed { id: 123, ext: Some(Cow::Owned("FuuExt".into())) }, name.extend(false, Some("fuu")));
198    /// ``````
199    #[must_use]
200    pub fn extend<I>(mut self, mut replace: bool, iter: I) -> Self
201    where
202        I: IntoIterator,
203        I::Item: Display,
204    {
205        for s in iter {
206            let s = s.to_string().to_pascal_case();
207            match &mut self {
208                Self::Named(name) => *name = Cow::Owned(format!("{s}{}", name.to_pascal_case())),
209                Self::Unnamed { ext: Some(ext), .. } if !take(&mut replace) => {
210                    *ext = Cow::Owned(format!("{s}{}", ext.to_pascal_case()));
211                }
212                Self::Unnamed { ext, .. } => {
213                    *ext = Some(Cow::Owned(s));
214                }
215            }
216        }
217
218        self
219    }
220}
221
222impl Display for Name {
223    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
224        match self {
225            Self::Named(x) => write!(f, "{x}"),
226            Self::Unnamed { id, ext: None } => write!(f, "Unnamed{id}"),
227            Self::Unnamed { id, ext: Some(ext) } => write!(f, "{ext}Unnamed{id}"),
228        }
229    }
230}
231
232impl From<String> for Name {
233    fn from(value: String) -> Self {
234        Self::Named(Cow::Owned(value))
235    }
236}
237
238impl From<&'static str> for Name {
239    fn from(value: &'static str) -> Self {
240        Self::Named(Cow::Borrowed(value))
241    }
242}