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