xsd_parser/pipeline/optimizer/
merge_enum_unions.rs1use crate::models::{
2 meta::{EnumerationMeta, EnumerationMetaVariant, MetaTypeVariant, UnionMeta, UnionMetaType},
3 TypeIdent,
4};
5use crate::traits::VecHelper;
6
7use super::{Error, Optimizer};
8
9impl Optimizer {
10 #[doc = include_str!("../../../tests/optimizer/union_flatten.xsd")]
25 #[doc = include_str!("../../../tests/optimizer/expected0/merge_enum_unions.rs")]
30 #[doc = include_str!("../../../tests/optimizer/expected1/merge_enum_unions.rs")]
35 pub fn merge_enum_union(mut self, ident: TypeIdent) -> Result<Self, Error> {
37 tracing::debug!("merge_enum_union(ident={ident:?})");
38
39 let Some(variant) = self.types.get_variant(&ident) else {
40 return Err(Error::UnknownType(ident));
41 };
42
43 let MetaTypeVariant::Union(_) = variant else {
44 return Err(Error::ExpectedUnion(ident));
45 };
46
47 let mut next = None;
48
49 self.merge_enum_union_impl(&ident, None, &mut next);
50
51 if let Some(next) = next {
52 let ty = self.types.items.get_mut(&ident).unwrap();
53 ty.variant = next;
54 }
55
56 Ok(self)
57 }
58
59 pub fn merge_enum_unions(mut self) -> Self {
63 tracing::debug!("merge_enum_unions");
64
65 let idents = self
66 .types
67 .items
68 .iter()
69 .filter_map(|(ident, type_)| {
70 if matches!(&type_.variant, MetaTypeVariant::Union(_)) {
71 Some(ident)
72 } else {
73 None
74 }
75 })
76 .cloned()
77 .collect::<Vec<_>>();
78
79 for ident in idents {
80 self = self.merge_enum_union(ident).unwrap();
81 }
82
83 self
84 }
85
86 fn merge_enum_union_impl(
87 &self,
88 ident: &TypeIdent,
89 display_name: Option<&str>,
90 next: &mut Option<MetaTypeVariant>,
91 ) {
92 let Some(type_) = self.types.get_variant(ident) else {
93 return;
94 };
95
96 match type_ {
97 MetaTypeVariant::Union(x) => {
98 for t in &*x.types {
99 self.merge_enum_union_impl(&t.type_, t.display_name.as_deref(), next);
100 }
101 }
102 MetaTypeVariant::Enumeration(x) => {
103 *next = match next.take() {
104 None => Some(MetaTypeVariant::Enumeration(EnumerationMeta::default())),
105 Some(MetaTypeVariant::Enumeration(ei)) => {
106 Some(MetaTypeVariant::Enumeration(ei))
107 }
108 Some(MetaTypeVariant::Union(ui)) => {
109 let mut ei = EnumerationMeta::default();
110
111 for t in ui.types.0 {
112 let var =
113 ei.variants
114 .find_or_insert(t.type_.to_property_ident(), |ident| {
115 EnumerationMetaVariant::new(ident)
116 .with_type(Some(t.type_.clone()))
117 });
118 var.display_name = t.display_name;
119 }
120
121 Some(MetaTypeVariant::Enumeration(ei))
122 }
123 _ => crate::unreachable!(),
124 };
125
126 let Some(MetaTypeVariant::Enumeration(ei)) = next else {
127 crate::unreachable!();
128 };
129
130 for var in &*x.variants {
131 let new_var = ei.variants.find_or_insert(var.ident.clone(), |ident| {
132 EnumerationMetaVariant::new(ident).with_type(var.type_.clone())
133 });
134 new_var.display_name.clone_from(&var.display_name);
135 }
136 }
137 MetaTypeVariant::Reference(x) if x.is_simple() => {
138 self.merge_enum_union_impl(&x.type_, display_name, next);
139 }
140 _ => {
141 if next.is_none() {
142 *next = Some(MetaTypeVariant::Union(UnionMeta::default()));
143 }
144
145 match next {
146 Some(MetaTypeVariant::Union(ui)) => {
147 let mut ti = UnionMetaType::new(ident.clone());
148 ti.display_name = display_name.map(ToOwned::to_owned);
149
150 ui.types.push(ti);
151 }
152 Some(MetaTypeVariant::Enumeration(ei)) => {
153 let var = ei.variants.find_or_insert(ident.to_property_ident(), |x| {
154 EnumerationMetaVariant::new(x).with_type(Some(ident.clone()))
155 });
156 var.display_name = display_name.map(ToOwned::to_owned);
157 }
158 _ => crate::unreachable!(),
159 }
160 }
161 }
162 }
163}