xsd_parser/pipeline/optimizer/
merge_enum_unions.rs1use crate::models::{
2 meta::{EnumerationMeta, EnumerationMetaVariant, MetaTypeVariant, UnionMeta, UnionMetaType},
3 Ident,
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: Ident) -> 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: &Ident,
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 = ei.variants.find_or_insert(t.type_.clone(), |ident| {
113 EnumerationMetaVariant::new(ident).with_type(Some(t.type_.clone()))
114 });
115 var.display_name = t.display_name;
116 }
117
118 Some(MetaTypeVariant::Enumeration(ei))
119 }
120 _ => crate::unreachable!(),
121 };
122
123 let Some(MetaTypeVariant::Enumeration(ei)) = next else {
124 crate::unreachable!();
125 };
126
127 for var in &*x.variants {
128 let new_var = ei.variants.find_or_insert(var.ident.clone(), |ident| {
129 EnumerationMetaVariant::new(ident).with_type(var.type_.clone())
130 });
131 new_var.display_name.clone_from(&var.display_name);
132 }
133 }
134 MetaTypeVariant::Reference(x) if x.is_single() => {
135 self.merge_enum_union_impl(&x.type_, display_name, next);
136 }
137 _ => {
138 if next.is_none() {
139 *next = Some(MetaTypeVariant::Union(UnionMeta::default()));
140 }
141
142 match next {
143 Some(MetaTypeVariant::Union(ui)) => {
144 let mut ti = UnionMetaType::new(ident.clone());
145 ti.display_name = display_name.map(ToOwned::to_owned);
146
147 ui.types.push(ti);
148 }
149 Some(MetaTypeVariant::Enumeration(ei)) => {
150 let var = ei.variants.find_or_insert(ident.clone(), |x| {
151 EnumerationMetaVariant::new(x).with_type(Some(ident.clone()))
152 });
153 var.display_name = display_name.map(ToOwned::to_owned);
154 }
155 _ => crate::unreachable!(),
156 }
157 }
158 }
159 }
160}