xsd_parser/optimizer/
merge_choice_cardinality.rs

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