Skip to main content

xsd_parser/models/naming/
mod.rs

1mod default;
2mod explicit;
3
4use std::borrow::Cow;
5
6use inflector::Inflector;
7use proc_macro2::Ident as Ident2;
8use quote::format_ident;
9
10use crate::config::MetaType;
11use crate::models::meta::MetaTypeVariant;
12use crate::models::schema::MaxOccurs;
13use crate::TypeIdent;
14
15use super::Name;
16
17pub use self::default::{NameBuilder, Naming};
18pub use self::explicit::{ExplicitNameBuilder, ExplicitNaming};
19
20/// Make a type name based on the passed `ident` and `ty` and the provided `postfixes`.
21pub fn make_type_name<X>(naming: &X, postfixes: &[String], ty: &MetaType, ident: &TypeIdent) -> Name
22where
23    X: crate::traits::Naming,
24{
25    if let MetaTypeVariant::Reference(ti) = &ty.variant {
26        if ident.name.is_generated() && ti.type_.name.is_named() {
27            let s = naming.format_type_name(ti.type_.name.as_str());
28
29            if ti.max_occurs > MaxOccurs::Bounded(1) {
30                return Name::new_generated(format!("{s}List"));
31            } else if ti.min_occurs == 0 {
32                return Name::new_generated(format!("{s}Opt"));
33            }
34        }
35    }
36
37    let postfix = postfixes
38        .get(ident.type_ as usize)
39        .map_or("", |s| s.as_str());
40
41    let s = naming.format_type_name(ident.name.as_str());
42
43    if s.ends_with(postfix) {
44        ident.name.clone()
45    } else {
46        Name::new_generated(format!("{s}{postfix}"))
47    }
48}
49
50/// Unify the passed string `s` into a standard format.
51#[must_use]
52pub fn unify_string(s: &str) -> String {
53    let mut done = true;
54    let s = s.replace(
55        |c: char| {
56            let replace = !c.is_alphanumeric();
57            if c != '_' && !replace {
58                done = false;
59            }
60
61            c != '_' && replace
62        },
63        "_",
64    );
65
66    if done {
67        s
68    } else {
69        s.to_screaming_snake_case().to_pascal_case()
70    }
71}
72
73/// Format an unknown variant identifier based on the passed `id`.
74#[must_use]
75pub fn format_unknown_variant(id: usize) -> Ident2 {
76    format_ident!("Unknown{id}")
77}
78
79/// Format the passed string `s` into a valid Rust identifier by adding an
80/// underscore if it starts with a numeric character or is a Rust keyword.
81#[must_use]
82pub fn format_ident<'a, S>(s: S) -> String
83where
84    S: Into<Cow<'a, str>>,
85{
86    let mut s = s.into();
87    if let Ok(idx) = KEYWORDS.binary_search_by(|(key, _)| key.cmp(&&*s)) {
88        s = Cow::Borrowed(KEYWORDS[idx].1);
89    }
90
91    if s.starts_with(char::is_numeric) {
92        s = Cow::Owned(format!("_{s}"));
93    }
94
95    s.replace(|c: char| !c.is_alphanumeric(), "_")
96}
97
98/// List of keywords that needs to be replaced by something else.
99/// This list needs to be sorted, because we use it in [`core::slice::binary_search_by`]
100const KEYWORDS: &[(&str, &str)] = &[
101    ("Self", "Self_"),
102    ("abstract", "abstract_"),
103    ("as", "as_"),
104    ("become", "become_"),
105    ("box", "box_"),
106    ("break", "break_"),
107    ("const", "const_"),
108    ("continue", "continue_"),
109    ("crate", "crate_"),
110    ("do", "do_"),
111    ("else", "else_"),
112    ("enum", "enum_"),
113    ("extern", "extern_"),
114    ("false", "false_"),
115    ("final", "final_"),
116    ("fn", "fn_"),
117    ("for", "for_"),
118    ("if", "if_"),
119    ("impl", "impl_"),
120    ("in", "in_"),
121    ("let", "let_"),
122    ("loop", "loop_"),
123    ("macro", "macro_"),
124    ("match", "match_"),
125    ("mod", "mod_"),
126    ("move", "move_"),
127    ("mut", "mut_"),
128    ("override", "override_"),
129    ("priv", "priv_"),
130    ("pub", "pub_"),
131    ("ref", "ref_"),
132    ("return", "return_"),
133    ("self", "self_"),
134    ("static", "static_"),
135    ("struct", "struct_"),
136    ("super", "super_"),
137    ("trait", "trait_"),
138    ("true", "true_"),
139    ("try", "try_"),
140    ("type", "type_"),
141    ("typeof", "typeof_"),
142    ("union", "union_"),
143    ("unsafe", "unsafe_"),
144    ("unsized", "unsized_"),
145    ("use", "use_"),
146    ("virtual", "virtual_"),
147    ("where", "where_"),
148    ("while", "while_"),
149    ("yield", "yield_"),
150];
151
152#[cfg(test)]
153mod tests {
154    use super::KEYWORDS;
155
156    #[test]
157    fn verify_keyword_order() {
158        for i in 1..KEYWORDS.len() {
159            assert!(dbg!(KEYWORDS[i - 1].0) < dbg!(KEYWORDS[i].0));
160        }
161    }
162}