Skip to main content

xsd_parser/models/naming/
default.rs

1use std::any::Any;
2use std::sync::{
3    atomic::{AtomicUsize, Ordering},
4    Arc,
5};
6
7use inflector::Inflector;
8use proc_macro2::Ident as Ident2;
9
10use crate::models::{
11    meta::{MetaType, MetaTypeVariant},
12    schema::MaxOccurs,
13    TypeIdent,
14};
15use crate::traits::{NameBuilder as NameBuilderTrait, Naming as NamingTrait};
16
17use super::Name;
18
19/// Default name generation and formatting implementation.
20///
21/// This type implements the [`Naming`](NamingTrait) trait that is used for
22/// naming generation and formatting.
23#[derive(Default, Debug, Clone)]
24pub struct Naming(Arc<AtomicUsize>);
25
26impl NamingTrait for Naming {
27    fn clone_boxed(&self) -> Box<dyn NamingTrait> {
28        Box::new(self.clone())
29    }
30
31    fn builder(&self) -> Box<dyn NameBuilderTrait> {
32        Box::new(NameBuilder::new(self.0.clone(), self.clone_boxed()))
33    }
34
35    fn unify(&self, s: &str) -> String {
36        super::unify_string(s)
37    }
38
39    fn make_type_name(&self, postfixes: &[String], ty: &MetaType, ident: &TypeIdent) -> Name {
40        if let MetaTypeVariant::Reference(ti) = &ty.variant {
41            if ident.name.is_generated() && ti.type_.name.is_named() {
42                let s = self.format_type_name(ti.type_.name.as_str());
43
44                if ti.max_occurs > MaxOccurs::Bounded(1) {
45                    return Name::new_generated(format!("{s}List"));
46                } else if ti.min_occurs == 0 {
47                    return Name::new_generated(format!("{s}Opt"));
48                }
49            }
50        }
51
52        let postfix = postfixes
53            .get(ident.type_ as usize)
54            .map_or("", |s| s.as_str());
55
56        let s = self.format_type_name(ident.name.as_str());
57
58        if s.ends_with(postfix) {
59            ident.name.clone()
60        } else {
61            Name::new_generated(format!("{s}{postfix}"))
62        }
63    }
64
65    fn make_unknown_variant(&self, id: usize) -> Ident2 {
66        super::format_unknown_variant(id)
67    }
68
69    fn format_module_name(&self, s: &str) -> String {
70        let s = self.unify(s).to_snake_case();
71
72        super::format_ident(s)
73    }
74
75    fn format_type_name(&self, s: &str) -> String {
76        let s = self.unify(s);
77
78        super::format_ident(s)
79    }
80
81    fn format_field_name(&self, s: &str) -> String {
82        let s = self.unify(s).to_snake_case();
83
84        super::format_ident(s)
85    }
86
87    fn format_variant_name(&self, s: &str) -> String {
88        let s = self.unify(s);
89
90        super::format_ident(s)
91    }
92
93    fn format_constant_name(&self, s: &str) -> String {
94        let s = self.unify(s).to_screaming_snake_case();
95
96        super::format_ident(s)
97    }
98}
99
100/// Default implementation for the [`NameBuilder`](NameBuilderTrait) trait.
101#[must_use]
102#[derive(Debug)]
103pub struct NameBuilder {
104    id: Arc<AtomicUsize>,
105    my_id: Option<usize>,
106    with_id: bool,
107    generated: bool,
108
109    name: Option<String>,
110    extension: Option<String>,
111
112    naming: Box<dyn NamingTrait>,
113}
114
115impl NameBuilder {
116    /// Create a new [`NameBuilder`] instance.
117    ///
118    /// The passed `id` is used to generate unique ids for unnamed types.
119    pub fn new(id: Arc<AtomicUsize>, naming: Box<dyn NamingTrait>) -> Self {
120        Self {
121            id,
122            my_id: None,
123            with_id: true,
124            generated: false,
125            name: None,
126            extension: None,
127            naming,
128        }
129    }
130}
131
132impl Clone for NameBuilder {
133    fn clone(&self) -> Self {
134        Self {
135            id: self.id.clone(),
136            my_id: self.my_id,
137            with_id: self.with_id,
138            generated: self.generated,
139            name: self.name.clone(),
140            extension: self.extension.clone(),
141            naming: self.naming.clone_boxed(),
142        }
143    }
144}
145
146impl NameBuilderTrait for NameBuilder {
147    fn finish(&self) -> Name {
148        let Self {
149            id,
150            my_id,
151            with_id,
152            mut generated,
153            name,
154            extension,
155            naming,
156        } = self;
157
158        let mut ret = String::new();
159        if let Some(s) = extension {
160            generated = true;
161            ret.push_str(&naming.unify(s));
162        }
163
164        if let Some(s) = name {
165            if ret.is_empty() {
166                ret.push_str(s);
167            } else {
168                ret.push_str(&naming.unify(s));
169            }
170        }
171
172        if ret.is_empty() {
173            generated = true;
174            ret.push_str("Unnamed");
175        }
176
177        if *with_id {
178            generated = true;
179            let id = my_id.unwrap_or_else(|| id.fetch_add(1, Ordering::Relaxed));
180            ret = format!("{ret}{id}");
181        }
182
183        if generated {
184            Name::new_generated(ret)
185        } else {
186            Name::new_named(ret)
187        }
188    }
189
190    fn merge(&mut self, other: &dyn NameBuilderTrait) {
191        let other: &Self = (other as &dyn Any).downcast_ref().unwrap();
192
193        if let Some(name) = other.name.clone() {
194            self.name.get_or_insert(name);
195            self.with_id = other.with_id;
196            self.generated = other.generated;
197
198            if let Some(id) = other.my_id {
199                self.with_id = other.with_id;
200                self.my_id.get_or_insert(id);
201            }
202
203            if let Some(ext) = other.extension.clone() {
204                self.extension.get_or_insert(ext);
205            }
206        }
207    }
208
209    fn clone_boxed(&self) -> Box<dyn NameBuilderTrait> {
210        Box::new(self.clone())
211    }
212
213    fn has_name(&self) -> bool {
214        self.name.is_some()
215    }
216
217    fn has_extension(&self) -> bool {
218        self.extension.is_some()
219    }
220
221    fn set_name(&mut self, name: String) {
222        self.name = Some(name);
223    }
224
225    fn set_with_id(&mut self, value: bool) {
226        self.with_id = value;
227    }
228
229    fn set_generated(&mut self, value: bool) {
230        self.generated = value;
231    }
232
233    fn add_extension(&mut self, replace: bool, extension: String) {
234        let s = self.naming.unify(&extension);
235
236        if replace {
237            self.extension = Some(s);
238        } else if let Some(prefix) = &self.extension {
239            self.extension = Some(format!("{s}{prefix}"));
240        } else {
241            self.extension = Some(s);
242        }
243    }
244
245    fn strip_suffix(&mut self, suffix: &str) {
246        if let Some(s) = &mut self.name {
247            if let Some(x) = s.strip_suffix(suffix) {
248                *s = x.into();
249            }
250        }
251
252        if let Some(s) = &mut self.extension {
253            if let Some(x) = s.strip_suffix(suffix) {
254                *s = x.into();
255            }
256        }
257    }
258
259    fn generate_unique_id(&mut self) {
260        if self.my_id.is_none() {
261            self.my_id = Some(self.id.fetch_add(1, Ordering::Release));
262        }
263    }
264
265    fn prepare_type_name(&mut self) {
266        self.strip_suffix("Type");
267        self.strip_suffix("Content");
268    }
269
270    fn prepare_field_name(&mut self) {
271        self.strip_suffix("Type");
272        self.strip_suffix("Content");
273    }
274
275    fn prepare_content_type_name(&mut self) {
276        self.strip_suffix("Type");
277        self.strip_suffix("Content");
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use crate::traits::Naming as _;
284
285    use super::Naming;
286
287    #[test]
288    fn default_naming() {
289        let naming = Naming::default();
290
291        assert_eq!("_", naming.unify("+"));
292        assert_eq!("FuuBarBaz", naming.unify("Fuu_BAR_BAZ"));
293        assert_eq!("FuuBarBaz", naming.unify("fuu_bar_baz"));
294        assert_eq!("FuuBarBaz", naming.unify("fuu+Bar-BAZ"));
295
296        assert_eq!("QName", naming.unify("QName"));
297        assert_eq!("QName", naming.format_type_name("QName"));
298        assert_eq!("QName", naming.format_variant_name("QName"));
299        assert_eq!("q_name", naming.format_field_name("QName"));
300    }
301}