xsd_parser/traits/
naming.rs

1use std::any::Any;
2use std::fmt::{Debug, Display};
3use std::mem::take;
4
5use inflector::Inflector;
6use proc_macro2::Ident as Ident2;
7use quote::format_ident;
8
9use crate::models::{
10    meta::{MetaType, MetaTypeVariant, MetaTypes},
11    schema::{MaxOccurs, NamespaceId},
12    Ident, Name,
13};
14
15/// This trait defined how names are generated and formatted in `xsd-parser`.
16///
17/// Use the [`Interpreter::with_naming`](crate::Interpreter::with_naming) method
18/// to use a customized implementation for this trait.
19pub trait Naming: Debug {
20    /// Clone this object into a new `Box`.
21    fn clone_boxed(&self) -> Box<dyn Naming>;
22
23    /// Create a new name builder instance.
24    fn builder(&self) -> Box<dyn NameBuilder>;
25
26    /// Unify the given string.
27    ///
28    /// Before actual name generation or formatting it is sometimes useful
29    /// to have a pre-formatting for names, to have a unified schema for the
30    /// names in general.
31    ///
32    /// The default implementation uses pascal case to unify all different kind
33    /// of names.
34    fn unify(&self, s: &str) -> String {
35        let mut done = true;
36        let s = s.replace(
37            |c: char| {
38                let replace = !c.is_alphanumeric();
39                if c != '_' && !replace {
40                    done = false;
41                }
42
43                c != '_' && replace
44            },
45            "_",
46        );
47
48        if done {
49            s
50        } else {
51            s.to_screaming_snake_case().to_pascal_case()
52        }
53    }
54
55    /// Format the passed string `s` as type name.
56    ///
57    /// The default implementation uses pascal case here.
58    fn format_type_name(&self, s: &str) -> String {
59        let mut name = self.unify(s);
60
61        if let Ok(idx) = KEYWORDS.binary_search_by(|(key, _)| key.cmp(&&*name)) {
62            name = KEYWORDS[idx].1.into();
63        }
64
65        if name.starts_with(char::is_numeric) {
66            name = format!("_{name}");
67        }
68
69        name
70    }
71
72    /// Format the passed string `s` as field name.
73    ///
74    /// The default implementation uses snake case here.
75    fn format_field_name(&self, s: &str) -> String {
76        let mut name = self.unify(s).to_snake_case();
77
78        if let Ok(idx) = KEYWORDS.binary_search_by(|(key, _)| key.cmp(&&*name)) {
79            name = KEYWORDS[idx].1.into();
80        }
81
82        if name.starts_with(char::is_numeric) {
83            name = format!("_{name}");
84        }
85
86        name
87    }
88
89    /// Format the passed string `s` as variant name.
90    ///
91    /// The default implementation uses [`format_type_name`](Naming::format_type_name) here.
92    fn format_variant_name(&self, s: &str) -> String {
93        self.format_type_name(s)
94    }
95
96    /// Format the passed string `s` as module name.
97    ///
98    /// The default implementation uses [`format_field_name`](Naming::format_field_name) here.
99    fn format_module_name(&self, s: &str) -> String {
100        self.format_field_name(s)
101    }
102
103    /// Create a suitable identifier for the passed type name `name` respecting
104    /// user defined names stored in `display_name`.
105    ///
106    /// The default implementation uses the `display_name` or `name` as fallback
107    /// and formats it using [`format_type_name`](Naming::format_type_name).
108    fn format_type_ident(&self, name: &Name, display_name: Option<&str>) -> Ident2 {
109        let ident = self.format_type_name(display_name.unwrap_or(name.as_str()));
110
111        format_ident!("{ident}")
112    }
113
114    /// Create a suitable identifier for the passed field name `name` respecting
115    /// user defined names stored in `display_name`.
116    ///
117    /// The default implementation uses the `display_name` or `name` as fallback
118    /// and formats it using [`format_field_name`](Naming::format_field_name).
119    fn format_field_ident(&self, name: &Name, display_name: Option<&str>) -> Ident2 {
120        let ident = self.format_field_name(display_name.unwrap_or(name.as_str()));
121
122        format_ident!("{ident}")
123    }
124
125    /// Create a suitable identifier for the passed variant name `name` respecting
126    /// user defined names stored in `display_name`.
127    ///
128    /// The default implementation uses [`format_type_ident`](Naming::format_type_ident) here.
129    fn format_variant_ident(&self, name: &Name, display_name: Option<&str>) -> Ident2 {
130        self.format_type_ident(name, display_name)
131    }
132
133    /// Create a suitable identifier for the passed module name `name`.
134    ///
135    /// The default implementation uses [`format_module_ident`](Naming::format_module_ident)
136    /// with `display_name` set to `None` here.
137    fn format_module_ident(&self, name: &Name) -> Ident2 {
138        self.format_field_ident(name, None)
139    }
140
141    /// Generate a identifier for the module identified by `ns`.
142    fn format_module(&self, types: &MetaTypes, ns: Option<NamespaceId>) -> Option<Ident2> {
143        let ns = ns?;
144        let module = types.modules.get(&ns)?;
145        let name = module.name.as_ref()?;
146
147        Some(self.format_module_ident(name))
148    }
149
150    /// Generate a name for the passed type `ty` identified by `ident` respecting the
151    /// configured type postfixes.
152    fn make_type_name(&self, postfixes: &[String], ty: &MetaType, ident: &Ident) -> Name {
153        if let MetaTypeVariant::Reference(ti) = &ty.variant {
154            if ident.name.is_generated() && ti.type_.name.is_named() {
155                let s = self.format_type_name(ti.type_.name.as_str());
156
157                if ti.max_occurs > MaxOccurs::Bounded(1) {
158                    return Name::new_generated(format!("{s}List"));
159                } else if ti.min_occurs == 0 {
160                    return Name::new_generated(format!("{s}Opt"));
161                }
162            }
163        }
164
165        let postfix = postfixes
166            .get(ident.type_ as usize)
167            .map_or("", |s| s.as_str());
168
169        let s = self.format_type_name(ident.name.as_str());
170
171        if s.ends_with(postfix) {
172            ident.name.clone()
173        } else {
174            Name::new_generated(format!("{s}{postfix}"))
175        }
176    }
177
178    /// Create an unknown enum variant using the provided id.
179    ///
180    /// The default implementation generated `Unknown{id}` here.
181    fn make_unknown_variant(&self, id: usize) -> Ident2 {
182        format_ident!("Unknown{id}")
183    }
184}
185
186/// This trait defines a builder for building type names.
187///
188/// The general idea of the builder is the following:
189/// - A type name needs a name to be valid. The name is set by
190///   [`set_name`](NameBuilder::set_name).
191/// - The name can be extended by multiple prefixes using
192///   [`add_extension`](NameBuilder::add_extension).
193/// - Sometimes is it not possible to create a unique name. To get one
194///   the builder should add a unique id to the name (this is controlled by
195///   [`set_with_id`](NameBuilder::set_with_id)).
196/// - The output of the name builder is a [`Name`]. `Name`s can be fixed or
197///   generated. The name builder should decide automatically which variant of
198///   the name has to be generated, if not explicitly specified by the user
199///   (using [`set_generated`](NameBuilder::set_generated)).
200pub trait NameBuilder: Debug + Any {
201    /// Finish the current name building and create a [`Name`] from the known
202    /// information.
203    fn finish(&self) -> Name;
204
205    /// Merge the data of the `other` name builder into the current name builder.
206    /// The passed name builder is of the same type then the current one.
207    fn merge(&mut self, other: &dyn NameBuilder);
208
209    /// Create a clone of the current builder and return it as box.
210    fn clone_boxed(&self) -> Box<dyn NameBuilder>;
211
212    /// Returns `true` if this builder has a name set, `false` otherwise.
213    fn has_name(&self) -> bool;
214
215    /// Returns `true` if this builder has at least on extension set, `false` otherwise.
216    fn has_extension(&self) -> bool;
217
218    /// Set the base name of this builder.
219    fn set_name(&mut self, name: String);
220
221    /// Instruct the builder to add a unique id to the generated name or not.
222    fn set_with_id(&mut self, value: bool);
223
224    /// Instruct the builder to generated a [`Name::Generated`] if `true` is passed,
225    /// or a [`Name::Named`] if `false` is passed.
226    fn set_generated(&mut self, value: bool);
227
228    /// Add a new `extension` to the builder. If `replace` is set to true, any previous
229    /// extension is dropped.
230    fn add_extension(&mut self, replace: bool, extension: String);
231
232    /// Remove the specified `suffix` from the name and the extensions.
233    fn strip_suffix(&mut self, suffix: &str);
234
235    /// Force the builder to generate a unique id, that is later used to generate
236    /// the name.
237    ///
238    /// Normally the id should be generated as last step (in the
239    /// [`finish`](NameBuilder::finish) method), but sometimes it is useful to
240    /// force the builder to generate the id to shared it between different copies
241    /// of the builder.
242    fn generate_unique_id(&mut self);
243}
244
245impl NameBuilder for Box<dyn NameBuilder> {
246    #[inline]
247    fn finish(&self) -> Name {
248        (**self).finish()
249    }
250
251    #[inline]
252    fn merge(&mut self, other: &dyn NameBuilder) {
253        (**self).merge(other);
254    }
255
256    #[inline]
257    fn clone_boxed(&self) -> Box<dyn NameBuilder> {
258        (**self).clone_boxed()
259    }
260
261    #[inline]
262    fn has_name(&self) -> bool {
263        (**self).has_name()
264    }
265
266    #[inline]
267    fn has_extension(&self) -> bool {
268        (**self).has_extension()
269    }
270
271    #[inline]
272    fn set_name(&mut self, name: String) {
273        (**self).set_name(name);
274    }
275
276    #[inline]
277    fn set_with_id(&mut self, value: bool) {
278        (**self).set_with_id(value);
279    }
280
281    #[inline]
282    fn set_generated(&mut self, value: bool) {
283        (**self).set_generated(value);
284    }
285
286    #[inline]
287    fn add_extension(&mut self, replace: bool, extension: String) {
288        (**self).add_extension(replace, extension);
289    }
290
291    #[inline]
292    fn strip_suffix(&mut self, suffix: &str) {
293        (**self).strip_suffix(suffix);
294    }
295
296    #[inline]
297    fn generate_unique_id(&mut self) {
298        (**self).generate_unique_id();
299    }
300}
301
302/// Helper trait that provides additional builder patterns to the [`NameBuilder`].
303pub trait NameBuilderExt: Sized {
304    /// Force the builder to generate a unique id.
305    #[must_use]
306    fn generate_id(self) -> Self;
307
308    /// Tell the builder to add (`true`) or not to add (`false`) the unique id
309    /// to the generated name.
310    #[must_use]
311    fn with_id(self, value: bool) -> Self;
312
313    /// Add extensions to the builder using the passed iterator `iter`. If `replace`
314    /// is set to `true` any existing extension is dropped before the new ones are
315    /// applied.
316    #[must_use]
317    fn extend<I>(self, replace: bool, iter: I) -> Self
318    where
319        I: IntoIterator,
320        I::Item: Display;
321
322    /// Remove the specified `suffix` from the builder.
323    #[must_use]
324    fn remove_suffix(self, suffix: &str) -> Self;
325
326    /// Instruct the builder to create a unique name from the passed `value`.
327    ///
328    /// This means, that the [`with_id`](NameBuilderExt::with_id) is automatically
329    /// set to `false`.
330    #[must_use]
331    fn unique_name<T>(self, value: T) -> Self
332    where
333        T: Display;
334
335    /// Instruct the builder to create a name that is shared between different parts
336    /// of the code from the passed `value`.
337    ///
338    /// This means, that the [`with_id`](NameBuilderExt::with_id) is automatically
339    /// set to `true` and the generated name has a unique id to be identified.
340    #[must_use]
341    fn shared_name<T>(self, value: T) -> Self
342    where
343        T: Display;
344
345    /// If the builder does currently not have a name, the passed `fallback` is applied.
346    #[must_use]
347    fn or<T>(self, fallback: T) -> Self
348    where
349        T: NameFallback;
350
351    /// If the builder does currently not have a name, the passed `fallback` is applied.
352    #[must_use]
353    fn or_else<F, T>(self, fallback: F) -> Self
354    where
355        F: FnOnce() -> T,
356        T: NameFallback;
357}
358
359impl<X> NameBuilderExt for X
360where
361    X: NameBuilder + Sized,
362{
363    fn generate_id(mut self) -> Self {
364        self.generate_unique_id();
365
366        self
367    }
368
369    fn with_id(mut self, value: bool) -> Self {
370        self.set_with_id(value);
371
372        self
373    }
374
375    fn extend<I>(mut self, mut replace: bool, iter: I) -> Self
376    where
377        I: IntoIterator,
378        I::Item: Display,
379    {
380        for ext in iter {
381            let ext = ext.to_string();
382
383            self.add_extension(take(&mut replace), ext);
384        }
385
386        self
387    }
388
389    fn remove_suffix(mut self, suffix: &str) -> Self {
390        self.strip_suffix(suffix);
391
392        self
393    }
394
395    fn unique_name<T>(mut self, value: T) -> Self
396    where
397        T: Display,
398    {
399        self.set_name(value.to_string());
400        self.set_with_id(false);
401
402        self
403    }
404
405    fn shared_name<T>(mut self, value: T) -> Self
406    where
407        T: Display,
408    {
409        self.set_name(value.to_string());
410        self.set_with_id(true);
411
412        self
413    }
414
415    fn or<T>(self, fallback: T) -> Self
416    where
417        T: NameFallback,
418    {
419        self.or_else(|| fallback)
420    }
421
422    fn or_else<F, T>(mut self, fallback: F) -> Self
423    where
424        F: FnOnce() -> T,
425        T: NameFallback,
426    {
427        if !self.has_name() {
428            fallback().apply(&mut self);
429        }
430
431        self
432    }
433}
434
435/// Helper trait used in [`NameBuilderExt::or`] and [`NameBuilderExt::or_else`] to define
436/// what can be used as fallback for a name.
437pub trait NameFallback {
438    /// Apply the fallback to the passed `builder`.
439    fn apply(self, builder: &mut dyn NameBuilder);
440}
441
442impl NameFallback for &dyn NameBuilder {
443    fn apply(self, builder: &mut dyn NameBuilder) {
444        builder.merge(self);
445    }
446}
447
448impl NameFallback for Box<dyn NameBuilder> {
449    fn apply(self, builder: &mut dyn NameBuilder) {
450        builder.merge(&*self);
451    }
452}
453
454impl NameFallback for Name {
455    #[inline]
456    fn apply(self, builder: &mut dyn NameBuilder) {
457        (&self).apply(builder);
458    }
459}
460
461impl NameFallback for &Name {
462    #[inline]
463    fn apply(self, builder: &mut dyn NameBuilder) {
464        builder.set_name(self.as_str().to_owned());
465        builder.set_generated(self.is_generated());
466        builder.set_with_id(false);
467    }
468}
469
470impl NameFallback for Option<&Name> {
471    #[inline]
472    fn apply(self, builder: &mut dyn NameBuilder) {
473        if let Some(x) = self {
474            x.apply(builder);
475        }
476    }
477}
478
479impl NameFallback for Option<Name> {
480    #[inline]
481    fn apply(self, builder: &mut dyn NameBuilder) {
482        self.as_ref().apply(builder);
483    }
484}
485
486impl NameFallback for &Option<Name> {
487    fn apply(self, builder: &mut dyn NameBuilder) {
488        self.as_ref().apply(builder);
489    }
490}
491
492impl NameFallback for &String {
493    fn apply(self, builder: &mut dyn NameBuilder) {
494        builder.set_name(self.to_owned());
495        builder.set_with_id(false);
496    }
497}
498
499impl NameFallback for Option<&String> {
500    fn apply(self, builder: &mut dyn NameBuilder) {
501        if let Some(x) = self {
502            x.apply(builder);
503        }
504    }
505}
506
507impl NameFallback for Option<String> {
508    fn apply(self, builder: &mut dyn NameBuilder) {
509        self.as_ref().apply(builder);
510    }
511}
512
513impl NameFallback for &Option<String> {
514    fn apply(self, builder: &mut dyn NameBuilder) {
515        self.as_ref().apply(builder);
516    }
517}
518
519impl NameFallback for &str {
520    fn apply(self, builder: &mut dyn NameBuilder) {
521        builder.set_name(self.to_owned());
522        builder.set_with_id(false);
523    }
524}
525
526impl NameFallback for Option<&str> {
527    fn apply(self, builder: &mut dyn NameBuilder) {
528        if let Some(x) = self {
529            x.apply(builder);
530        }
531    }
532}
533
534/// List of keywords that needs to be replaced by something else.
535/// This list needs to be sorted, because we use it in [`core::slice::binary_search_by`]
536const KEYWORDS: &[(&str, &str)] = &[
537    ("Self", "Self_"),
538    ("abstract", "abstract_"),
539    ("as", "as_"),
540    ("become", "become_"),
541    ("box", "box_"),
542    ("break", "break_"),
543    ("const", "const_"),
544    ("continue", "continue_"),
545    ("crate", "crate_"),
546    ("do", "do_"),
547    ("else", "else_"),
548    ("enum", "enum_"),
549    ("extern", "extern_"),
550    ("false", "false_"),
551    ("final", "final_"),
552    ("fn", "fn_"),
553    ("for", "for_"),
554    ("if", "if_"),
555    ("impl", "impl_"),
556    ("in", "in_"),
557    ("let", "let_"),
558    ("loop", "loop_"),
559    ("macro", "macro_"),
560    ("match", "match_"),
561    ("mod", "mod_"),
562    ("move", "move_"),
563    ("mut", "mut_"),
564    ("override", "override_"),
565    ("priv", "priv_"),
566    ("pub", "pub_"),
567    ("ref", "ref_"),
568    ("return", "return_"),
569    ("self", "self_"),
570    ("static", "static_"),
571    ("struct", "struct_"),
572    ("super", "super_"),
573    ("trait", "trait_"),
574    ("true", "true_"),
575    ("try", "try_"),
576    ("type", "type_"),
577    ("typeof", "typeof_"),
578    ("union", "union_"),
579    ("unsafe", "unsafe_"),
580    ("unsized", "unsized_"),
581    ("use", "use_"),
582    ("virtual", "virtual_"),
583    ("where", "where_"),
584    ("while", "while_"),
585    ("yield", "yield_"),
586];
587
588#[cfg(test)]
589mod tests {
590    use super::KEYWORDS;
591
592    #[test]
593    fn verify_keyword_order() {
594        for i in 1..KEYWORDS.len() {
595            assert!(dbg!(KEYWORDS[i - 1].0) < dbg!(KEYWORDS[i].0));
596        }
597    }
598}