Skip to main content

xsd_parser/pipeline/generator/
context.rs

1use std::collections::BTreeMap;
2use std::ops::Deref;
3
4use bit_set::BitSet;
5use proc_macro2::{Literal, TokenStream};
6use quote::{format_ident, quote};
7use xsd_parser_types::xml::NamespacesShared;
8
9use crate::config::GeneratorFlags;
10use crate::models::{
11    code::{ModuleIdent, ModulePath},
12    data::Occurs,
13    meta::{BuildInMeta, MetaTypeVariant},
14    schema::xs::Use,
15    TypeIdent,
16};
17use crate::pipeline::generator::ValueGeneratorMode;
18use crate::pipeline::renderer::{Context as RendererContext, ValueRendererBox};
19
20use super::{Error, MetaData, State, TraitInfos, TypeRef};
21
22/// Helper type that is used to request the code generation for a specific type.
23#[derive(Debug)]
24pub struct Context<'a, 'types> {
25    /// Meta data with different information of the generate process.
26    pub meta: &'a MetaData<'types>,
27
28    /// Identifier of the type that is currently processed.
29    pub ident: &'a TypeIdent,
30
31    state: &'a mut State<'types>,
32    reachable: BitSet<u64>,
33    namespaces: Vec<NamespacesShared<'static>>,
34}
35
36impl<'a, 'types> Context<'a, 'types> {
37    /// Get the namespaces that are currently in scope.
38    #[must_use]
39    pub fn namespaces(&self) -> Option<&NamespacesShared<'static>> {
40        self.namespaces.last()
41    }
42
43    pub(super) fn new(
44        meta: &'a MetaData<'types>,
45        ident: &'a TypeIdent,
46        state: &'a mut State<'types>,
47    ) -> Self {
48        let reachable = state.loop_detection.get_reachable(&state.cache, ident);
49
50        Self {
51            meta,
52            ident,
53            state,
54            reachable,
55            namespaces: Vec::new(),
56        }
57    }
58
59    pub(super) fn current_module(&self) -> ModuleIdent {
60        ModuleIdent::new(
61            self.meta.types,
62            self.ident,
63            self.check_generator_flags(GeneratorFlags::USE_NAMESPACE_MODULES),
64            self.check_generator_flags(GeneratorFlags::USE_SCHEMA_MODULES),
65        )
66    }
67
68    pub(super) fn current_type_ref(&self) -> &TypeRef {
69        self.state.cache.get(self.ident).unwrap()
70    }
71
72    pub(super) fn get_trait_infos(&mut self) -> &TraitInfos {
73        self.state
74            .trait_infos
75            .get_or_insert_with(|| TraitInfos::new(self.meta.types))
76    }
77
78    pub(super) fn get_or_create_type_ref(&mut self, ident: &TypeIdent) -> Result<&TypeRef, Error> {
79        let type_ref = self.state.get_or_create_type_ref_mut(self.meta, ident)?;
80
81        Ok(type_ref)
82    }
83
84    pub(super) fn get_or_create_type_ref_for_value(
85        &mut self,
86        ident: &TypeIdent,
87        by_value: bool,
88    ) -> Result<&TypeRef, Error> {
89        let type_ref = self.state.get_or_create_type_ref_mut(self.meta, ident)?;
90
91        if by_value {
92            type_ref.reachable.union_with(&self.reachable);
93        }
94
95        Ok(type_ref)
96    }
97
98    pub(super) fn get_or_create_type_ref_for_element(
99        &mut self,
100        ident: &TypeIdent,
101        by_value: bool,
102    ) -> Result<(&TypeRef, bool), Error> {
103        let boxed = by_value && need_box(&mut self.reachable, &self.state.cache, self.meta, ident);
104        let type_ref = self.state.get_or_create_type_ref_mut(self.meta, ident)?;
105
106        if !boxed {
107            type_ref.reachable.union_with(&self.reachable);
108        }
109
110        Ok((type_ref, boxed))
111    }
112
113    pub(super) fn make_trait_impls(&mut self) -> Result<Vec<TokenStream>, Error> {
114        let ident = self.ident.clone();
115        let current_module = self.current_module();
116        let module_path = ModulePath::from_ident(self.types, current_module);
117
118        self.get_trait_infos()
119            .get(&ident)
120            .into_iter()
121            .flat_map(|info| &info.traits_all)
122            .cloned()
123            .collect::<Vec<_>>()
124            .into_iter()
125            .map(|ident| {
126                let type_ref = self.get_or_create_type_ref(&ident)?;
127                let ident = format_ident!("{}Trait", type_ref.path.ident());
128                let trait_type = (*type_ref.path).clone().with_ident(ident);
129                let trait_ident = trait_type.relative_to(&module_path);
130
131                Ok(trait_ident)
132            })
133            .collect::<Result<Vec<_>, _>>()
134    }
135
136    #[allow(clippy::too_many_lines)]
137    pub(super) fn make_value_renderer(
138        &mut self,
139        ident: &TypeIdent,
140        value: &str,
141        mode: ValueGeneratorMode,
142    ) -> Result<ValueRendererBox, Error> {
143        use ValueGeneratorMode::Constant as C;
144        use ValueGeneratorMode::Value as V;
145
146        let types = self.types;
147        let ty = types
148            .items
149            .get(ident)
150            .ok_or_else(|| Error::UnknownType(ident.clone()))?;
151        let type_ref = self.get_or_create_type_ref(ident)?;
152
153        macro_rules! build_in {
154            ($ty:ty) => {
155                if let Ok(val) = value.parse::<$ty>() {
156                    return Ok(Box::new(if mode == ValueGeneratorMode::Literal {
157                        let val = Literal::byte_string(value.as_bytes());
158
159                        quote!(#val)
160                    } else {
161                        quote!(#val)
162                    }));
163                }
164            };
165        }
166
167        match (mode, &ty.variant) {
168            (_, MetaTypeVariant::BuildIn(BuildInMeta::U8)) => build_in!(u8),
169            (_, MetaTypeVariant::BuildIn(BuildInMeta::U16)) => build_in!(u16),
170            (_, MetaTypeVariant::BuildIn(BuildInMeta::U32)) => build_in!(u32),
171            (_, MetaTypeVariant::BuildIn(BuildInMeta::U64)) => build_in!(u64),
172            (_, MetaTypeVariant::BuildIn(BuildInMeta::U128)) => build_in!(u128),
173            (_, MetaTypeVariant::BuildIn(BuildInMeta::Usize)) => build_in!(usize),
174
175            (_, MetaTypeVariant::BuildIn(BuildInMeta::I8)) => build_in!(i8),
176            (_, MetaTypeVariant::BuildIn(BuildInMeta::I16)) => build_in!(i16),
177            (_, MetaTypeVariant::BuildIn(BuildInMeta::I32)) => build_in!(i32),
178            (_, MetaTypeVariant::BuildIn(BuildInMeta::I64)) => build_in!(i64),
179            (_, MetaTypeVariant::BuildIn(BuildInMeta::I128)) => build_in!(i128),
180            (_, MetaTypeVariant::BuildIn(BuildInMeta::Isize)) => build_in!(isize),
181
182            (C | V, MetaTypeVariant::BuildIn(BuildInMeta::F32)) => build_in!(f32),
183            (C | V, MetaTypeVariant::BuildIn(BuildInMeta::F64)) => build_in!(f64),
184
185            (C | V, MetaTypeVariant::BuildIn(BuildInMeta::Bool)) => {
186                match value.to_ascii_lowercase().as_str() {
187                    "true" | "yes" | "1" => return Ok(Box::new(quote!(true))),
188                    "false" | "no" | "0" => return Ok(Box::new(quote!(false))),
189                    _ => (),
190                }
191            }
192            (_, MetaTypeVariant::BuildIn(BuildInMeta::Str)) => {
193                return Ok(Box::new(if mode == ValueGeneratorMode::Literal {
194                    let val = Literal::byte_string(value.as_bytes());
195
196                    quote!(#val)
197                } else {
198                    quote!(#value)
199                }));
200            }
201            (V, MetaTypeVariant::BuildIn(BuildInMeta::String)) => {
202                let value = value.to_string();
203                let string = self
204                    .get_or_create_type_ref(&TypeIdent::STRING)?
205                    .path
206                    .clone();
207
208                return Ok(Box::new(move |ctx: &RendererContext<'_, '_>| {
209                    let string = ctx.resolve_type_for_module(&string);
210
211                    quote!(#string::from(#value))
212                }));
213            }
214
215            (mode, MetaTypeVariant::Custom(x)) => {
216                if let Some(default) = &x.default {
217                    return default.exec(self, value, mode);
218                }
219            }
220
221            (mode, MetaTypeVariant::Enumeration(ei)) => {
222                let target_type = type_ref.path.clone();
223
224                for var in &*ei.variants {
225                    if var.type_.is_none() && var.ident.name.as_str() == value {
226                        let variant_ident = self
227                            .types
228                            .naming
229                            .format_variant_ident(&var.ident.name, var.display_name.as_deref());
230
231                        return Ok(Box::new(move |ctx: &RendererContext<'_, '_>| {
232                            let target_type = ctx.resolve_type_for_module(&target_type);
233
234                            quote!(#target_type :: #variant_ident)
235                        }));
236                    }
237
238                    if let Some(target_ident) = &var.type_ {
239                        if let Ok(inner) = self.make_value_renderer(target_ident, value, mode) {
240                            let variant_ident = match self.state.cache.get(target_ident) {
241                                Some(type_ref) if var.ident.name.is_generated() => {
242                                    type_ref.path.ident().clone()
243                                }
244                                _ => self.types.naming.format_variant_ident(
245                                    &var.ident.name,
246                                    var.display_name.as_deref(),
247                                ),
248                            };
249
250                            return Ok(Box::new(move |ctx: &RendererContext<'_, '_>| {
251                                let inner = inner.render(ctx);
252                                let target_type = ctx.resolve_type_for_module(&target_type);
253
254                                quote!(#target_type :: #variant_ident(#inner))
255                            }));
256                        }
257                    }
258                }
259            }
260
261            (mode, MetaTypeVariant::Union(ui)) => {
262                let target_type = type_ref.path.clone();
263
264                for ty in &*ui.types {
265                    if let Ok(inner) = self.make_value_renderer(&ty.type_, value, mode) {
266                        let variant_ident = match self.state.cache.get(&ty.type_) {
267                            Some(type_ref) if ty.type_.name.is_generated() => {
268                                type_ref.path.ident().clone()
269                            }
270                            _ => self
271                                .types
272                                .naming
273                                .format_variant_ident(&ty.type_.name, ty.display_name.as_deref()),
274                        };
275
276                        return Ok(Box::new(move |ctx: &RendererContext<'_, '_>| {
277                            let inner = inner.render(ctx);
278                            let target_type = ctx.resolve_type_for_module(&target_type);
279
280                            quote!(#target_type :: #variant_ident ( #inner ))
281                        }));
282                    }
283                }
284            }
285
286            (mode, MetaTypeVariant::Reference(ti)) => {
287                match Occurs::from_occurs(ti.min_occurs, ti.max_occurs) {
288                    Occurs::Single => return self.make_value_renderer(&ti.type_, value, mode),
289                    Occurs::DynamicList
290                        if value.is_empty() && mode == ValueGeneratorMode::Value =>
291                    {
292                        let target_type = type_ref.path.clone();
293
294                        return Ok(Box::new(move |ctx: &RendererContext<'_, '_>| {
295                            let vec = ctx.resolve_build_in("::alloc::vec::Vec");
296                            let target_type = ctx.resolve_type_for_module(&target_type);
297
298                            quote! { #target_type(#vec::new()) }
299                        }));
300                    }
301                    _ => (),
302                }
303            }
304
305            (mode, MetaTypeVariant::SimpleType(si)) => {
306                let target_type = type_ref.path.clone();
307                let inner = self.make_value_renderer(&si.base, value, mode)?;
308
309                return Ok(Box::new(move |ctx: &RendererContext<'_, '_>| {
310                    let inner = inner.render(ctx);
311                    let target_type = ctx.resolve_type_for_module(&target_type);
312
313                    quote! { #target_type(#inner) }
314                }));
315            }
316
317            _ => (),
318        }
319
320        Err(Error::InvalidDefaultValue {
321            ident: ident.clone(),
322            value: value.to_owned(),
323            mode,
324        })
325    }
326
327    pub(super) fn push_namespaces(&mut self, namespaces: NamespacesShared<'static>) {
328        self.namespaces.push(namespaces);
329    }
330
331    pub(super) fn pop_namespaces(&mut self) {
332        self.namespaces.pop();
333    }
334}
335
336impl<'types> Deref for Context<'_, 'types> {
337    type Target = MetaData<'types>;
338
339    fn deref(&self) -> &Self::Target {
340        self.meta
341    }
342}
343
344fn need_box(
345    reachable: &mut BitSet<u64>,
346    cache: &BTreeMap<TypeIdent, TypeRef>,
347    meta: &MetaData<'_>,
348    ident: &TypeIdent,
349) -> bool {
350    let Some(ty) = meta.types.items.get(ident) else {
351        return false;
352    };
353
354    let Some(type_ref) = cache.get(ident) else {
355        return false;
356    };
357
358    if !reachable.insert(type_ref.id) {
359        return true;
360    }
361
362    let mut ret = false;
363
364    match &ty.variant {
365        MetaTypeVariant::Reference(x) => {
366            let occurs = Occurs::from_occurs(x.min_occurs, x.max_occurs);
367
368            if occurs.is_direct() {
369                ret = need_box(reachable, cache, meta, &x.type_);
370            }
371        }
372        MetaTypeVariant::Union(x) => {
373            for var in x.types.iter() {
374                ret = ret || need_box(reachable, cache, meta, &var.type_);
375            }
376        }
377        MetaTypeVariant::Enumeration(x) => {
378            for var in x.variants.iter() {
379                if let Some(type_) = &var.type_ {
380                    if var.use_ != Use::Prohibited {
381                        ret = ret || need_box(reachable, cache, meta, type_);
382                    }
383                }
384            }
385        }
386        MetaTypeVariant::All(_)
387        | MetaTypeVariant::Choice(_)
388        | MetaTypeVariant::Sequence(_)
389        | MetaTypeVariant::ComplexType(_)
390        | MetaTypeVariant::Dynamic(_)
391        | MetaTypeVariant::SimpleType(_)
392        | MetaTypeVariant::BuildIn(_)
393        | MetaTypeVariant::Custom(_) => (),
394    }
395
396    reachable.remove(type_ref.id);
397
398    ret
399}