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}