xsd_parser/optimizer/
dynamic_to_choice.rs

1use crate::types::{
2    ComplexInfo, ElementInfo, ElementMode, GroupInfo, Ident, Type, TypeVariant, VecHelper,
3};
4
5use super::Optimizer;
6
7impl Optimizer {
8    /// This will use a enum that contains all known variants of the dynamic
9    /// type instead of a dynamic box.
10    ///
11    /// # Examples
12    ///
13    /// Consider the following XML schema.
14    /// ```xml
15    #[doc = include_str!("../../tests/optimizer/abstract.xsd")]
16    /// ```
17    ///
18    /// Without this optimization this will result in the following code:
19    /// ```rust
20    #[doc = include_str!("../../tests/optimizer/expected0/convert_dynamic_to_choice.rs")]
21    /// ```
22    ///
23    /// With this optimization the following code is generated:
24    /// ```rust
25    #[doc = include_str!("../../tests/optimizer/expected1/convert_dynamic_to_choice.rs")]
26    /// ```
27    pub fn convert_dynamic_to_choice(mut self) -> Self {
28        use std::collections::btree_map::Entry;
29
30        tracing::debug!("convert_dynamic_to_choice");
31
32        let idents = self
33            .types
34            .iter()
35            .filter_map(|(ident, ty)| {
36                if matches!(&ty.variant, TypeVariant::Dynamic(_)) {
37                    Some(ident)
38                } else {
39                    None
40                }
41            })
42            .cloned()
43            .collect::<Vec<_>>();
44
45        for ident in idents {
46            let content_ident = Ident::new(self.types.make_unnamed()).with_ns(ident.ns);
47
48            let type_ = self.types.get_mut(&ident).unwrap();
49            let TypeVariant::Dynamic(x) = &mut type_.variant else {
50                crate::unreachable!();
51            };
52
53            let mut si = GroupInfo::default();
54            for derived in &x.derived_types {
55                si.elements.find_or_insert(derived.clone(), |ident| {
56                    ElementInfo::new(ident, derived.clone(), ElementMode::Element)
57                });
58            }
59
60            type_.variant = TypeVariant::ComplexType(ComplexInfo {
61                content: Some(content_ident.clone()),
62                is_dynamic: true,
63                ..Default::default()
64            });
65
66            match self.types.entry(content_ident) {
67                Entry::Vacant(e) => {
68                    e.insert(Type::new(TypeVariant::Choice(si)));
69                }
70                Entry::Occupied(_) => crate::unreachable!(),
71            }
72        }
73
74        self
75    }
76}