xsd_parser/pipeline/optimizer/mod.rs
1//! Type-level optimization utilities for reducing schema complexity.
2//!
3//! This module defines the [`Optimizer`] and supporting logic for transforming
4//! a [`MetaTypes`] structure into a simpler, cleaner, and smaller form.
5//!
6//! Optimizations include:
7//! - Flattening nested complex types
8//! - Removing unused or duplicate unions/enums
9//! - Resolving typedef chains
10//! - Normalizing choice cardinalities
11//! - Simplifying unrestricted base types
12//!
13//! These transformations are especially useful before code generation, to avoid
14//! verbose or redundant Rust output and ensure efficient, idiomatic structures.
15
16mod dynamic_to_choice;
17mod empty_enums;
18mod empty_unions;
19mod flatten_complex_type;
20mod flatten_unions;
21mod merge_choice_cardinality;
22mod merge_enum_unions;
23mod misc;
24mod remove_duplicates;
25mod resolve_typedefs;
26mod simplify_mixed_types;
27mod unrestricted_base;
28
29use thiserror::Error;
30
31use crate::models::{meta::MetaTypes, Ident};
32
33use self::misc::{BaseMap, TypedefMap};
34
35pub use self::unrestricted_base::UnrestrictedBaseFlags;
36
37/// Optimizes a [`MetaTypes`] structure by reducing redundant or verbose type definitions.
38///
39/// The [`Optimizer`] performs various semantic transformations on a type graph,
40/// such as flattening unions, removing empty enums, simplifying typedef chains,
41/// and reducing nested complex structures.
42///
43/// It is typically used after schema interpretation and before code generation,
44/// to ensure that only necessary and well-structured types are preserved in the final output.
45///
46/// Optimization is performed lazily; the resulting [`MetaTypes`] can be retrieved
47/// using [`finish`](Self::finish).
48#[must_use]
49#[derive(Debug)]
50pub struct Optimizer {
51 types: MetaTypes,
52 bases: Option<BaseMap>,
53 typedefs: Option<TypedefMap>,
54}
55
56/// Error that is raised by the [`Optimizer`].
57#[derive(Error, Debug)]
58pub enum Error {
59 /// Unknown type identifier.
60 ///
61 /// Is raised if a specific identifier could not be resolved to it's
62 /// corresponding type information.
63 #[error("Unknown type identifier: {0}!")]
64 UnknownType(Ident),
65
66 /// The type is not a union type.
67 ///
68 /// Is raised if a type is expected to be a union, but it is not.
69 #[error("The type is not a union type: {0}!")]
70 ExpectedUnion(Ident),
71
72 /// The type is not a complex choice type.
73 ///
74 /// Is raised if a type is expected to be a complex choice, but it is not.
75 #[error("The type is not a complex choice type: {0}!")]
76 ExpectedComplexChoice(Ident),
77
78 /// The type is not a complex type.
79 ///
80 /// Is raised if a type is expected to be a complex type, but it is not.
81 #[error("The type is not a complex type: {0}!")]
82 ExpectedComplexType(Ident),
83
84 /// The complex type is missing a content type.
85 ///
86 /// Is raised if the content type of a complex type could not be resolved.
87 #[error("Complex type {0} is missing a content type!")]
88 MissingContentType(Ident),
89
90 /// The complex type is expected to have a choice content.
91 ///
92 /// Is raised if the content type of a complex type it not a choice.
93 #[error("Complex type {0} is expected to have a choice content!")]
94 ExpectedChoiceContent(Ident),
95
96 /// The complex type is expected to have content with [`MaxOccurs::Unbounded`](crate::models::schema::MaxOccurs::Unbounded).
97 ///
98 /// Is raised if the content of a complex type does nor have unbounded occurrence.
99 #[error("Complex type {0} is expected to have content with unbound occurrence!")]
100 ExpectedUnboundContent(Ident),
101
102 /// The complex type has an unexpected content type.
103 ///
104 /// Is raised if the content type of a complex type does not match the expectations.
105 #[error("Complex type {0} has an unexpected content type!")]
106 UnexpectedContentType(Ident),
107
108 /// The complex type contains an unexpected element in it's content type.
109 ///
110 /// Is raised if any element of the content of a complex type does not match the
111 /// expectations.
112 #[error("Complex type {0} contains an unexpected element in it's content type!")]
113 UnexpectedElementInContent(Ident),
114
115 /// Custom Error
116 #[error("{0}")]
117 Custom(String),
118}
119
120macro_rules! get_bases {
121 ($this:expr) => {{
122 if $this.bases.is_none() {
123 $this.bases = Some(crate::pipeline::optimizer::BaseMap::new(&$this.types));
124 }
125
126 $this.bases.as_ref().unwrap()
127 }};
128}
129
130macro_rules! get_typedefs {
131 ($this:expr) => {{
132 if $this.typedefs.is_none() {
133 $this.typedefs = Some(crate::pipeline::optimizer::TypedefMap::new(&$this.types));
134 }
135
136 $this.typedefs.as_ref().unwrap()
137 }};
138}
139
140pub(super) use get_bases;
141pub(super) use get_typedefs;
142
143impl Optimizer {
144 /// Create a new [`Optimizer`] instance from the passed `types`.
145 pub fn new(types: MetaTypes) -> Self {
146 Self {
147 types,
148 bases: None,
149 typedefs: None,
150 }
151 }
152
153 /// Finish the optimization and return the resulting [`MetaTypes`].
154 #[must_use]
155 pub fn finish(self) -> MetaTypes {
156 self.types
157 }
158}