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