xsd_parser/pipeline/optimizer/
flatten_unions.rs1use crate::models::{
2 meta::{MetaTypeVariant, UnionMeta, UnionMetaType},
3 Ident,
4};
5
6use super::{Error, Optimizer};
7
8struct FlattenUnionInfo {
9 count: usize,
10 meta: UnionMeta,
11}
12
13impl Optimizer {
14 #[doc = include_str!("../../../tests/optimizer/union_flatten.xsd")]
26 #[doc = include_str!("../../../tests/optimizer/expected0/flatten_unions.rs")]
31 #[doc = include_str!("../../../tests/optimizer/expected1/flatten_unions.rs")]
36 pub fn flatten_union(mut self, ident: Ident) -> Result<Self, Error> {
38 tracing::debug!("flatten_union(ident={ident:?})");
39
40 let Some(ty) = self.types.items.get(&ident) else {
41 return Err(Error::UnknownType(ident));
42 };
43
44 let MetaTypeVariant::Union(ui) = &ty.variant else {
45 return Err(Error::ExpectedUnion(ident));
46 };
47
48 let mut info = FlattenUnionInfo {
49 count: 0,
50 meta: UnionMeta::default(),
51 };
52
53 self.flatten_union_impl(&ident, None, &mut info);
54
55 if info.count > 1 {
56 info.meta.base = ui.base.clone();
57
58 let ty = self.types.items.get_mut(&ident).unwrap();
59 ty.variant = MetaTypeVariant::Union(info.meta);
60 }
61
62 Ok(self)
63 }
64
65 pub fn flatten_unions(mut self) -> Self {
69 tracing::debug!("flatten_unions");
70
71 let idents = self
72 .types
73 .items
74 .iter()
75 .filter_map(|(ident, type_)| {
76 if matches!(&type_.variant, MetaTypeVariant::Union(_)) {
77 Some(ident)
78 } else {
79 None
80 }
81 })
82 .cloned()
83 .collect::<Vec<_>>();
84
85 for ident in idents {
86 self = self.flatten_union(ident).unwrap();
87 }
88
89 self
90 }
91
92 fn flatten_union_impl(
93 &self,
94 ident: &Ident,
95 display_name: Option<&str>,
96 next: &mut FlattenUnionInfo,
97 ) {
98 let Some(type_) = self.types.items.get(ident) else {
99 return;
100 };
101
102 match &type_.variant {
103 MetaTypeVariant::Union(x) => {
104 next.count += 1;
105 for t in &*x.types {
106 self.flatten_union_impl(&t.type_, t.display_name.as_deref(), next);
107 }
108 }
109 MetaTypeVariant::Reference(x) if x.is_single() => {
110 self.flatten_union_impl(&x.type_, display_name, next);
111 }
112 _ => {
113 let mut ui = UnionMetaType::new(ident.clone());
114 ui.display_name = display_name.map(ToOwned::to_owned);
115
116 next.meta.types.push(ui);
117 }
118 }
119 }
120}