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_name = self.types.name_builder().shared_name("Content").finish();
47            let content_ident = Ident::new(content_name).with_ns(ident.ns);
48
49            let type_ = self.types.get_mut(&ident).unwrap();
50            let TypeVariant::Dynamic(x) = &mut type_.variant else {
51                crate::unreachable!();
52            };
53
54            let mut si = GroupInfo::default();
55            for derived in &x.derived_types {
56                si.elements.find_or_insert(derived.clone(), |ident| {
57                    ElementInfo::new(ident, derived.clone(), ElementMode::Element)
58                });
59            }
60
61            type_.variant = TypeVariant::ComplexType(ComplexInfo {
62                content: Some(content_ident.clone()),
63                is_dynamic: true,
64                ..Default::default()
65            });
66
67            match self.types.entry(content_ident) {
68                Entry::Vacant(e) => {
69                    e.insert(Type::new(TypeVariant::Choice(si)));
70                }
71                Entry::Occupied(_) => crate::unreachable!(),
72            }
73        }
74
75        self
76    }
77}