Skip to main content

xsd_parser/pipeline/optimizer/
dynamic_to_choice.rs

1use crate::models::{
2    meta::{ComplexMeta, ElementMeta, ElementMode, GroupMeta, MetaType, MetaTypeVariant},
3    TypeIdent,
4};
5use crate::traits::{NameBuilderExt as _, VecHelper};
6
7use super::Optimizer;
8
9impl Optimizer {
10    /// This will use a enum that contains all known variants of the dynamic
11    /// type instead of a dynamic box.
12    ///
13    /// # Examples
14    ///
15    /// Consider the following XML schema.
16    /// ```xml
17    #[doc = include_str!("../../../tests/optimizer/abstract.xsd")]
18    /// ```
19    ///
20    /// Without this optimization this will result in the following code:
21    /// ```rust
22    #[doc = include_str!("../../../tests/optimizer/expected0/convert_dynamic_to_choice.rs")]
23    /// ```
24    ///
25    /// With this optimization the following code is generated:
26    /// ```rust
27    #[doc = include_str!("../../../tests/optimizer/expected1/convert_dynamic_to_choice.rs")]
28    /// ```
29    pub fn convert_dynamic_to_choice(mut self) -> Self {
30        use std::collections::btree_map::Entry;
31
32        tracing::debug!("convert_dynamic_to_choice");
33
34        let idents = self
35            .types
36            .items
37            .iter()
38            .filter_map(|(ident, ty)| {
39                if matches!(&ty.variant, MetaTypeVariant::Dynamic(_)) {
40                    Some(ident)
41                } else {
42                    None
43                }
44            })
45            .cloned()
46            .collect::<Vec<_>>();
47
48        for ident in idents {
49            let content_name = self.types.name_builder().shared_name("Content").finish();
50            let content_ident = TypeIdent::new(content_name).with_ns(ident.ns);
51
52            let mut si = GroupMeta::default();
53            let type_ = self.types.items.get(&ident).unwrap();
54            self.add_elements(&mut si, type_);
55
56            let type_ = self.types.items.get_mut(&ident).unwrap();
57            type_.variant = MetaTypeVariant::ComplexType(ComplexMeta {
58                content: Some(content_ident.clone()),
59                is_dynamic: true,
60                ..Default::default()
61            });
62
63            match self.types.items.entry(content_ident) {
64                Entry::Vacant(e) => {
65                    e.insert(MetaType::new(if si.elements.is_empty() {
66                        MetaTypeVariant::Sequence(si)
67                    } else {
68                        MetaTypeVariant::Choice(si)
69                    }));
70                }
71                Entry::Occupied(_) => crate::unreachable!(),
72            }
73        }
74
75        self
76    }
77
78    fn add_elements(&self, group: &mut GroupMeta, ty: &MetaType) {
79        let form = ty.form();
80        let MetaTypeVariant::Dynamic(x) = &ty.variant else {
81            crate::unreachable!();
82        };
83
84        for meta in &x.derived_types {
85            let derived_ty = self.types.get_resolved_type(&meta.type_).unwrap();
86            if let MetaTypeVariant::Dynamic(_) = &derived_ty.variant {
87                self.add_elements(group, derived_ty);
88            } else {
89                group
90                    .elements
91                    .find_or_insert(meta.type_.to_property_ident(), |ident| {
92                        let mut el =
93                            ElementMeta::new(ident, meta.type_.clone(), ElementMode::Element, form);
94
95                        if let Some(display_name) = &meta.display_name {
96                            el.display_name = Some(display_name.clone());
97                        }
98
99                        el
100                    });
101            }
102        }
103    }
104}