xsd_parser/pipeline/optimizer/
merge_choice_cardinality.rs

1use crate::models::{meta::MetaTypeVariant, schema::MaxOccurs, Ident};
2
3use super::{Error, Optimizer};
4
5impl Optimizer {
6    /// This will merge the cardinality of each element of the complex choice
7    /// type identified by `ident` with the cardinality of the types content.
8    ///
9    /// # Errors
10    ///
11    /// Returns an error if the passed `ident` could not be found, the referenced
12    /// type is not complex type or the complex types content is not a choice.
13    ///
14    /// # Examples
15    ///
16    /// Consider the following XML schema.
17    /// ```xml
18    #[doc = include_str!("../../../tests/optimizer/complex_choice.xsd")]
19    /// ```
20    ///
21    /// Without this optimization this will result in the following code:
22    /// ```rust
23    #[doc = include_str!("../../../tests/optimizer/expected0/merge_choice_cardinalities.rs")]
24    /// ```
25    ///
26    /// With this optimization the following code is generated:
27    /// ```rust
28    #[doc = include_str!("../../../tests/optimizer/expected1/merge_choice_cardinalities.rs")]
29    /// ```
30    pub fn merge_choice_cardinality(mut self, ident: Ident) -> Result<Self, Error> {
31        tracing::debug!("merge_choice_cardinality(ident={ident:?})");
32
33        let Some(ty) = self.types.get_variant(&ident) else {
34            return Err(Error::UnknownType(ident));
35        };
36
37        let MetaTypeVariant::ComplexType(ci) = ty else {
38            return Err(Error::ExpectedComplexType(ident));
39        };
40
41        let Some(content_ident) = ci.content.clone() else {
42            return Err(Error::MissingContentType(ident));
43        };
44
45        let Some(MetaTypeVariant::Choice(ci)) = self.types.get_variant_mut(&content_ident) else {
46            return Err(Error::ExpectedComplexChoice(ident));
47        };
48
49        let mut min = 1;
50        let mut max = MaxOccurs::Bounded(1);
51
52        for element in &mut *ci.elements {
53            min = min.min(element.min_occurs);
54            max = max.max(element.max_occurs);
55
56            element.min_occurs = 1;
57            element.max_occurs = MaxOccurs::Bounded(1);
58        }
59
60        let Some(MetaTypeVariant::ComplexType(ci)) = self.types.get_variant_mut(&ident) else {
61            unreachable!();
62        };
63
64        ci.min_occurs = min.min(ci.min_occurs);
65        ci.max_occurs = max.max(ci.max_occurs);
66
67        Ok(self)
68    }
69
70    /// This merge the cardinality of all elements of a choice with the content of the choice for
71    /// all choice types.
72    ///
73    /// For details see [`merge_choice_cardinality`](Self::merge_choice_cardinality).
74    pub fn merge_choice_cardinalities(mut self) -> Self {
75        tracing::debug!("merge_choice_cardinalities");
76
77        let idents = self
78            .types
79            .items
80            .iter()
81            .filter_map(|(ident, type_)| {
82                if matches!(&type_.variant, MetaTypeVariant::ComplexType(ci) if ci.has_complex_choice_content(&self.types)) {
83                    Some(ident)
84                } else {
85                    None
86                }
87            })
88            .cloned()
89            .collect::<Vec<_>>();
90
91        for ident in idents {
92            self = self.merge_choice_cardinality(ident).unwrap();
93        }
94
95        self
96    }
97}