xsd_parser/types/
name_builder.rs

1use std::fmt::Display;
2use std::mem::take;
3use std::sync::atomic::Ordering;
4use std::sync::{atomic::AtomicUsize, Arc};
5
6use super::Name;
7
8/// Builder type to construct a [`Name`].
9#[must_use]
10#[derive(Debug, Clone)]
11pub struct NameBuilder {
12    id: Arc<AtomicUsize>,
13    my_id: Option<usize>,
14    with_id: bool,
15
16    name: Option<String>,
17    extension: Option<String>,
18}
19
20impl NameBuilder {
21    /// Create a new [`NameBuilder`] instance.
22    ///
23    /// The passed `id` is used to generate unique ids for unnamed types.
24    pub fn new(id: Arc<AtomicUsize>) -> Self {
25        Self {
26            id,
27            my_id: None,
28            with_id: true,
29            name: None,
30            extension: None,
31        }
32    }
33
34    /// Finish the builder and create the [`Name`] instance.
35    #[must_use]
36    pub fn finish(self) -> Name {
37        let Self {
38            id,
39            my_id,
40            with_id,
41            name,
42            extension,
43        } = self;
44
45        let mut generated = false;
46
47        let mut ret = String::new();
48        if let Some(s) = extension {
49            generated = true;
50            ret.push_str(&Name::unify(&s));
51        }
52
53        if let Some(s) = name {
54            ret.push_str(&s);
55        }
56
57        if ret.is_empty() {
58            generated = true;
59            ret.push_str("Unnamed");
60        }
61
62        if with_id {
63            generated = true;
64            let id = my_id.unwrap_or_else(|| id.fetch_add(1, Ordering::Relaxed));
65            ret = format!("{ret}{id}");
66        }
67
68        if generated {
69            Name::new_generated(ret)
70        } else {
71            Name::new_named(ret)
72        }
73    }
74
75    /// Wether to add a unique id to the generated name or not.
76    pub fn with_id(mut self, value: bool) -> Self {
77        self.with_id = value;
78
79        self
80    }
81
82    /// Generate the id for the name.
83    ///
84    /// This can be useful if you want to clone the builder to generate multiple names
85    /// with the same id. For example for a field name and corresponding field type.
86    pub fn generate_id(mut self) -> Self {
87        if self.my_id.is_none() {
88            self.my_id = Some(self.id.fetch_add(1, Ordering::Release));
89        }
90
91        self
92    }
93
94    /// Set a unique name.
95    ///
96    /// This will automatically set `with_id` to `false`.
97    pub fn unique_name<T>(mut self, value: T) -> Self
98    where
99        T: Display,
100    {
101        self.name = Some(value.to_string());
102        self.with_id = false;
103
104        self
105    }
106
107    /// Set a shared name.
108    ///
109    /// This will automatically set `with_id` to `true`.
110    pub fn shared_name<T>(mut self, value: T) -> Self
111    where
112        T: Display,
113    {
114        self.name = Some(value.to_string());
115        self.with_id = true;
116
117        self
118    }
119
120    /// Uses the name that is already stored in the builder, or the passed
121    /// `fallback` value if the name was not set yet.
122    pub fn or<T>(self, fallback: T) -> Self
123    where
124        T: NameFallback,
125    {
126        self.or_else(|| fallback)
127    }
128
129    /// Uses the name that is already stored in the builder, or the value that
130    /// is returned by the passed `fallback` closure if the name was not set yet.
131    pub fn or_else<F, T>(mut self, fallback: F) -> Self
132    where
133        F: FnOnce() -> T,
134        T: NameFallback,
135    {
136        self.name = self.name.or_else(|| fallback().resolve());
137        if self.name.is_some() {
138            self.with_id = false;
139        }
140
141        self
142    }
143
144    /// Add a extension to the name.
145    ///
146    /// Extensions are added as to the generated name as prefix.
147    pub fn extend<I>(mut self, mut replace: bool, iter: I) -> Self
148    where
149        I: IntoIterator,
150        I::Item: Display,
151    {
152        for s in iter {
153            let s = s.to_string();
154            let s = Name::unify(&s);
155
156            if take(&mut replace) {
157                self.extension = Some(s);
158            } else if let Some(prefix) = &self.extension {
159                self.extension = Some(format!("{s}{prefix}"));
160            } else {
161                self.extension = Some(s);
162            }
163        }
164
165        self
166    }
167
168    /// Remove the specified `suffix` from the name and the extension.
169    pub fn remove_suffix(mut self, suffix: &str) -> Self {
170        if let Some(s) = &mut self.name {
171            if let Some(x) = s.strip_suffix(suffix) {
172                *s = x.into();
173            }
174        }
175
176        if let Some(s) = &mut self.extension {
177            if let Some(x) = s.strip_suffix(suffix) {
178                *s = x.into();
179            }
180        }
181
182        self
183    }
184
185    /// Returns `true` if a extension was specified, `false` otherwise.
186    #[inline]
187    #[must_use]
188    pub fn has_extension(&self) -> bool {
189        self.extension.is_some()
190    }
191}
192
193/// Helper trait to define fallback values passed to [`NameBuilder::or`] or
194/// [`NameBuilder::or_else`]
195pub trait NameFallback {
196    /// Create the fallback value.
197    fn resolve(self) -> Option<String>;
198}
199
200impl NameFallback for Name {
201    fn resolve(self) -> Option<String> {
202        Some(self.as_str().to_owned())
203    }
204}
205
206impl NameFallback for &String {
207    fn resolve(self) -> Option<String> {
208        Some(self.to_owned())
209    }
210}
211
212impl NameFallback for &Name {
213    fn resolve(self) -> Option<String> {
214        Some(self.as_str().to_owned())
215    }
216}
217
218impl<X> NameFallback for Option<X>
219where
220    X: Display,
221{
222    fn resolve(self) -> Option<String> {
223        self.map(|x| format!("{x}"))
224    }
225}
226
227impl<X> NameFallback for &Option<X>
228where
229    X: Display,
230{
231    fn resolve(self) -> Option<String> {
232        self.as_ref().map(|x| format!("{x}"))
233    }
234}