xsd_parser/pipeline/generator/
context.rs

1use std::ops::Deref;
2
3use proc_macro2::TokenStream;
4use quote::{format_ident, quote};
5
6use crate::config::GeneratorFlags;
7use crate::models::{
8    code::{format_variant_ident, ModuleIdent, ModulePath},
9    data::Occurs,
10    meta::{BuildInMeta, MetaTypeVariant},
11    Ident,
12};
13
14use super::{Error, MetaData, State, TraitInfos, TypeRef};
15
16/// Helper type that is used to request the code generation for a specific type.
17#[derive(Debug)]
18pub struct Context<'a, 'types> {
19    /// Meta data with different information of the generate process.
20    pub meta: &'a MetaData<'types>,
21
22    /// Identifier of the type that is currently processed.
23    pub ident: &'a Ident,
24
25    state: &'a mut State<'types>,
26}
27
28impl<'a, 'types> Context<'a, 'types> {
29    pub(super) fn new(
30        meta: &'a MetaData<'types>,
31        ident: &'a Ident,
32        state: &'a mut State<'types>,
33    ) -> Self {
34        Self { meta, ident, state }
35    }
36
37    pub(super) fn current_module(&self) -> ModuleIdent {
38        ModuleIdent::new(
39            self.meta.types,
40            self.ident,
41            self.check_generator_flags(GeneratorFlags::USE_NAMESPACE_MODULES),
42            self.check_generator_flags(GeneratorFlags::USE_SCHEMA_MODULES),
43        )
44    }
45
46    pub(super) fn current_type_ref(&self) -> &TypeRef {
47        self.state.cache.get(self.ident).unwrap()
48    }
49
50    pub(super) fn get_trait_infos(&mut self) -> &TraitInfos {
51        self.state
52            .trait_infos
53            .get_or_insert_with(|| TraitInfos::new(self.meta.types))
54    }
55
56    pub(super) fn get_or_create_type_ref(&mut self, ident: &Ident) -> Result<&TypeRef, Error> {
57        self.state.get_or_create_type_ref(self.meta, ident)
58    }
59
60    pub(super) fn make_trait_impls(&mut self) -> Result<Vec<TokenStream>, Error> {
61        let ident = self.ident.clone();
62        let current_module = self.current_module();
63        let module_path = ModulePath::from_ident(self.types, current_module);
64
65        self.get_trait_infos()
66            .get(&ident)
67            .into_iter()
68            .flat_map(|info| &info.traits_all)
69            .cloned()
70            .collect::<Vec<_>>()
71            .into_iter()
72            .map(|ident| {
73                let type_ref = self.get_or_create_type_ref(&ident)?;
74                let ident = format_ident!("{}Trait", type_ref.path.ident());
75                let trait_type = (*type_ref.path).clone().with_ident(ident);
76                let trait_ident = trait_type.relative_to(&module_path);
77
78                Ok(trait_ident)
79            })
80            .collect::<Result<Vec<_>, _>>()
81    }
82
83    #[allow(clippy::too_many_lines)]
84    pub(super) fn render_literal(
85        &mut self,
86        current_module: ModuleIdent,
87        default: &str,
88        ident: &Ident,
89    ) -> Result<TokenStream, Error> {
90        let types = self.types;
91        let ty = types
92            .items
93            .get(ident)
94            .ok_or_else(|| Error::UnknownType(ident.clone()))?;
95        let type_ref = self.get_or_create_type_ref(ident)?;
96
97        macro_rules! build_in {
98            ($ty:ty) => {
99                if let Ok(val) = default.parse::<$ty>() {
100                    return Ok(quote!(#val));
101                }
102            };
103        }
104
105        match &ty.variant {
106            MetaTypeVariant::BuildIn(BuildInMeta::U8) => build_in!(u8),
107            MetaTypeVariant::BuildIn(BuildInMeta::U16) => build_in!(u16),
108            MetaTypeVariant::BuildIn(BuildInMeta::U32) => build_in!(u32),
109            MetaTypeVariant::BuildIn(BuildInMeta::U64) => build_in!(u64),
110            MetaTypeVariant::BuildIn(BuildInMeta::U128) => build_in!(u128),
111            MetaTypeVariant::BuildIn(BuildInMeta::Usize) => build_in!(usize),
112
113            MetaTypeVariant::BuildIn(BuildInMeta::I8) => build_in!(i8),
114            MetaTypeVariant::BuildIn(BuildInMeta::I16) => build_in!(i16),
115            MetaTypeVariant::BuildIn(BuildInMeta::I32) => build_in!(i32),
116            MetaTypeVariant::BuildIn(BuildInMeta::I64) => build_in!(i64),
117            MetaTypeVariant::BuildIn(BuildInMeta::I128) => build_in!(i128),
118            MetaTypeVariant::BuildIn(BuildInMeta::Isize) => build_in!(isize),
119
120            MetaTypeVariant::BuildIn(BuildInMeta::F32) => build_in!(f32),
121            MetaTypeVariant::BuildIn(BuildInMeta::F64) => build_in!(f64),
122
123            MetaTypeVariant::BuildIn(BuildInMeta::Bool) => {
124                match default.to_ascii_lowercase().as_str() {
125                    "true" | "yes" | "1" => return Ok(quote!(true)),
126                    "false" | "no" | "0" => return Ok(quote!(false)),
127                    _ => (),
128                }
129            }
130            MetaTypeVariant::BuildIn(BuildInMeta::String) => {
131                return Ok(quote!(String::from(#default)));
132            }
133
134            MetaTypeVariant::Custom(x) => {
135                if let Some(x) = x.default(default) {
136                    return Ok(x);
137                }
138            }
139
140            MetaTypeVariant::Enumeration(ei) => {
141                let module_path = ModulePath::from_ident(types, current_module);
142                let target_type = type_ref.path.relative_to(&module_path);
143
144                for var in &*ei.variants {
145                    if var.type_.is_none() && var.ident.name.as_str() == default {
146                        let variant_ident =
147                            format_variant_ident(&var.ident.name, var.display_name.as_deref());
148
149                        return Ok(quote!(#target_type :: #variant_ident));
150                    }
151
152                    if let Some(target_ident) = &var.type_ {
153                        if let Ok(default) =
154                            self.render_literal(current_module, default, target_ident)
155                        {
156                            let variant_ident = match self.state.cache.get(target_ident) {
157                                Some(type_ref) if var.ident.name.is_generated() => {
158                                    type_ref.path.ident().clone()
159                                }
160                                _ => format_variant_ident(
161                                    &var.ident.name,
162                                    var.display_name.as_deref(),
163                                ),
164                            };
165
166                            return Ok(quote!(#target_type :: #variant_ident(#default)));
167                        }
168                    }
169                }
170            }
171
172            MetaTypeVariant::Union(ui) => {
173                let module_path = ModulePath::from_ident(types, current_module);
174                let target_type = type_ref.path.relative_to(&module_path);
175
176                for ty in &*ui.types {
177                    if let Ok(code) = self.render_literal(current_module, default, &ty.type_) {
178                        let variant_ident = match self.state.cache.get(&ty.type_) {
179                            Some(type_ref) if ty.type_.name.is_generated() => {
180                                type_ref.path.ident().clone()
181                            }
182                            _ => format_variant_ident(&ty.type_.name, ty.display_name.as_deref()),
183                        };
184
185                        return Ok(quote! {
186                            #target_type :: #variant_ident ( #code )
187                        });
188                    }
189                }
190            }
191
192            MetaTypeVariant::Reference(ti) => {
193                match Occurs::from_occurs(ti.min_occurs, ti.max_occurs) {
194                    Occurs::Single => {
195                        return self.render_literal(current_module, default, &ti.type_)
196                    }
197                    Occurs::DynamicList if default.is_empty() => {
198                        let module_path = ModulePath::from_ident(types, current_module);
199                        let target_type = type_ref.path.relative_to(&module_path);
200
201                        return Ok(quote! { #target_type(Vec::new()) });
202                    }
203                    _ => (),
204                }
205            }
206
207            MetaTypeVariant::SimpleType(si) => {
208                let module_path = ModulePath::from_ident(types, current_module);
209                let target_type = type_ref.path.relative_to(&module_path);
210                let default = self.render_literal(current_module, default, &si.base)?;
211
212                return Ok(quote! { #target_type(#default) });
213            }
214
215            _ => (),
216        }
217
218        Err(Error::InvalidDefaultValue(
219            ident.clone(),
220            default.to_owned(),
221        ))
222    }
223}
224
225impl<'types> Deref for Context<'_, 'types> {
226    type Target = MetaData<'types>;
227
228    fn deref(&self) -> &Self::Target {
229        self.meta
230    }
231}