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}