1use std::collections::{HashMap, HashSet};
4use std::hash::{Hash, Hasher};
5
6use crate::{
7 schema::{MaxOccurs, MinOccurs},
8 types::{
9 Base, ComplexInfo, ElementInfo, ElementMode, EnumerationInfo, GroupInfo, Ident, Name,
10 ReferenceInfo, Type, TypeEq, Types, UnionInfo, UnionTypeInfo, VariantInfo, VecHelper,
11 },
12};
13
14#[must_use]
21#[derive(Debug)]
22pub struct Optimizer {
23 types: Types,
24 bases: Option<BaseMap>,
25 typedefs: Option<TypedefMap>,
26}
27
28#[derive(Debug)]
29struct BaseMap(HashMap<Ident, Base>);
30
31#[derive(Debug)]
32struct TypedefMap(HashMap<Ident, Ident>);
33
34struct FlattenComplexInfo {
35 info: GroupInfo,
36 count: usize,
37 is_choice: bool,
38}
39
40struct FlattenUnionInfo {
41 count: usize,
42 info: UnionInfo,
43}
44
45macro_rules! get_bases {
46 ($this:expr) => {{
47 if $this.bases.is_none() {
48 $this.bases = Some(BaseMap::new(&$this.types));
49 }
50
51 $this.bases.as_ref().unwrap()
52 }};
53}
54
55macro_rules! get_typedefs {
56 ($this:expr) => {{
57 if $this.typedefs.is_none() {
58 $this.typedefs = Some(TypedefMap::new(&$this.types));
59 }
60
61 $this.typedefs.as_ref().unwrap()
62 }};
63}
64
65impl Optimizer {
66 pub fn new(types: Types) -> Self {
68 Self {
69 types,
70 bases: None,
71 typedefs: None,
72 }
73 }
74
75 #[must_use]
77 pub fn finish(self) -> Types {
78 self.types
79 }
80
81 #[doc = include_str!("../../tests/optimizer/enum_empty_variant.xsd")]
88 #[doc = include_str!("../../tests/optimizer/expected0/remove_empty_enum_variants.rs")]
93 #[doc = include_str!("../../tests/optimizer/expected1/remove_empty_enum_variants.rs")]
98 pub fn remove_empty_enum_variants(mut self) -> Self {
100 tracing::debug!("remove_empty_enum_variants");
101
102 for type_ in self.types.types.values_mut() {
103 if let Type::Enumeration(x) = type_ {
104 x.variants
105 .retain(|x| !matches!(&x.ident.name, Name::Named(x) if x.is_empty()));
106 }
107 }
108
109 self
110 }
111
112 #[doc = include_str!("../../tests/optimizer/enum_empty.xsd")]
123 #[doc = include_str!("../../tests/optimizer/expected0/remove_empty_enums.rs")]
128 #[doc = include_str!("../../tests/optimizer/expected1/remove_empty_enums.rs")]
134 pub fn remove_empty_enums(mut self) -> Self {
136 tracing::debug!("remove_empty_enums");
137
138 for type_ in self.types.types.values_mut() {
139 if let Type::Enumeration(x) = type_ {
140 if x.variants.is_empty() {
141 if let Some(base) = x.base.as_ident() {
142 self.typedefs = None;
143 *type_ = Type::Reference(ReferenceInfo::new(base.clone()));
144 }
145 }
146 }
147 }
148
149 self
150 }
151
152 #[doc = include_str!("../../tests/optimizer/union_duplicate.xsd")]
162 #[doc = include_str!("../../tests/optimizer/expected0/remove_duplicate_union_variants.rs")]
167 #[doc = include_str!("../../tests/optimizer/expected1/remove_duplicate_union_variants.rs")]
172 pub fn remove_duplicate_union_variants(mut self) -> Self {
174 tracing::debug!("remove_duplicate_union_variants");
175
176 let typedefs = get_typedefs!(self);
177
178 for type_ in self.types.types.values_mut() {
179 if let Type::Union(x) = type_ {
180 let mut i = 0;
181 let mut types_ = HashSet::new();
182
183 while i < x.types.len() {
184 let type_ = typedefs.resolve(&x.types[i].type_).clone();
185 if types_.insert(type_) {
186 i += 1;
187 } else {
188 x.types.remove(i);
189 }
190 }
191 }
192 }
193
194 self
195 }
196
197 #[doc = include_str!("../../tests/optimizer/union_empty.xsd")]
205 #[doc = include_str!("../../tests/optimizer/expected0/remove_empty_unions.rs")]
210 #[doc = include_str!("../../tests/optimizer/expected1/remove_empty_unions.rs")]
215 pub fn remove_empty_unions(mut self) -> Self {
217 tracing::debug!("remove_empty_unions");
218
219 for type_ in self.types.types.values_mut() {
220 if let Type::Union(x) = type_ {
221 if x.types.len() <= 1 {
222 let base = x.types.first().map(|x| &x.type_).or(x.base.as_ident());
223 if let Some(base) = base {
224 self.typedefs = None;
225 *type_ = Type::Reference(ReferenceInfo::new(base.clone()));
226 }
227 }
228 }
229 }
230
231 self
232 }
233
234 #[doc = include_str!("../../tests/optimizer/complex_restricted.xsd")]
248 #[doc = include_str!("../../tests/optimizer/expected0/use_unrestricted_base_type.rs")]
253 #[doc = include_str!("../../tests/optimizer/expected1/use_unrestricted_base_type.rs")]
258 pub fn use_unrestricted_base_type(mut self) -> Self {
260 tracing::debug!("use_unrestricted_base_type");
261
262 let bases = get_bases!(self);
263
264 for (ident, type_) in &mut *self.types {
265 match type_ {
266 Type::ComplexType(_) | Type::Enumeration(_) | Type::Union(_) => {
267 let base = bases.get_unrestricted(ident).clone();
268 if *ident != base {
269 self.typedefs = None;
270 *type_ = Type::Reference(ReferenceInfo::new(base));
271 }
272 }
273 _ => (),
274 }
275 }
276
277 self
278 }
279
280 #[doc = include_str!("../../tests/optimizer/abstract.xsd")]
288 #[doc = include_str!("../../tests/optimizer/expected0/convert_dynamic_to_choice.rs")]
293 #[doc = include_str!("../../tests/optimizer/expected1/convert_dynamic_to_choice.rs")]
298 pub fn convert_dynamic_to_choice(mut self) -> Self {
300 use std::collections::btree_map::Entry;
301
302 tracing::debug!("convert_dynamic_to_choice");
303
304 let idents = self
305 .types
306 .iter()
307 .filter_map(|(ident, ty)| {
308 if matches!(ty, Type::Dynamic(_)) {
309 Some(ident)
310 } else {
311 None
312 }
313 })
314 .cloned()
315 .collect::<Vec<_>>();
316
317 for ident in idents {
318 let content_ident = Ident::new(self.types.make_unnamed()).with_ns(ident.ns);
319
320 let type_ = self.types.get_mut(&ident).unwrap();
321 let Type::Dynamic(x) = type_ else {
322 crate::unreachable!();
323 };
324
325 let mut si = GroupInfo::default();
326 for derived in &x.derived_types {
327 si.elements.find_or_insert(derived.clone(), |ident| {
328 ElementInfo::new(ident, derived.clone(), ElementMode::Element)
329 });
330 }
331
332 *type_ = Type::ComplexType(ComplexInfo {
333 content: Some(content_ident.clone()),
334 is_dynamic: true,
335 ..Default::default()
336 });
337
338 match self.types.entry(content_ident) {
339 Entry::Vacant(e) => {
340 e.insert(Type::Choice(si));
341 }
342 Entry::Occupied(_) => crate::unreachable!(),
343 }
344 }
345
346 self
347 }
348
349 #[doc = include_str!("../../tests/optimizer/complex_flatten.xsd")]
357 #[doc = include_str!("../../tests/optimizer/expected0/flatten_element_content.rs")]
362 #[doc = include_str!("../../tests/optimizer/expected1/flatten_element_content.rs")]
367 pub fn flatten_element_content(mut self) -> Self {
369 tracing::debug!("flatten_element_content");
370
371 let idents = self
372 .types
373 .iter()
374 .filter_map(|(ident, type_)| {
375 if matches!(type_, Type::ComplexType(ci) if ci.has_complex_content(&self.types)) {
376 Some(ident)
377 } else {
378 None
379 }
380 })
381 .cloned()
382 .collect::<Vec<_>>();
383
384 for ident in idents {
385 let type_ = self.types.get(&ident).unwrap();
386 let Type::ComplexType(ci) = type_ else {
387 crate::unreachable!();
388 };
389 let Some(content_ident) = ci.content.clone() else {
390 continue;
391 };
392
393 let mut info = FlattenComplexInfo {
394 info: GroupInfo::default(),
395 count: 0,
396 is_choice: false,
397 };
398
399 self.flatten_complex(&content_ident, ci.min_occurs, ci.max_occurs, &mut info);
400
401 if info.count > 1 {
402 let type_ = if info.is_choice {
403 Type::Choice(info.info)
404 } else {
405 Type::Sequence(info.info)
406 };
407
408 self.types.insert(content_ident, type_);
409 }
410 }
411
412 self
413 }
414
415 #[doc = include_str!("../../tests/optimizer/union_flatten.xsd")]
422 #[doc = include_str!("../../tests/optimizer/expected0/flatten_unions.rs")]
427 #[doc = include_str!("../../tests/optimizer/expected1/flatten_unions.rs")]
432 pub fn flatten_unions(mut self) -> Self {
434 tracing::debug!("flatten_unions");
435
436 let idents = self
437 .types
438 .iter()
439 .filter_map(|(ident, type_)| {
440 if matches!(type_, Type::Union(_)) {
441 Some(ident)
442 } else {
443 None
444 }
445 })
446 .cloned()
447 .collect::<Vec<_>>();
448
449 for ident in idents {
450 let Some(Type::Union(x)) = self.types.get(&ident) else {
451 continue;
452 };
453 let mut info = FlattenUnionInfo {
454 count: 0,
455 info: UnionInfo::default(),
456 };
457 self.flatten_union(&ident, None, &mut info);
458 if info.count > 1 {
459 info.info.base = x.base.clone();
460
461 self.types.insert(ident, Type::Union(info.info));
462 }
463 }
464
465 self
466 }
467
468 #[doc = include_str!("../../tests/optimizer/union_flatten.xsd")]
475 #[doc = include_str!("../../tests/optimizer/expected0/merge_enum_unions.rs")]
480 #[doc = include_str!("../../tests/optimizer/expected1/merge_enum_unions.rs")]
485 pub fn merge_enum_unions(mut self) -> Self {
487 tracing::debug!("merge_enum_unions");
488
489 let idents = self
490 .types
491 .iter()
492 .filter_map(|(ident, type_)| {
493 if matches!(type_, Type::Union(_)) {
494 Some(ident)
495 } else {
496 None
497 }
498 })
499 .cloned()
500 .collect::<Vec<_>>();
501
502 for ident in idents {
503 let Some(Type::Union(_)) = self.types.get(&ident) else {
504 continue;
505 };
506 let mut next = None;
507 self.flatten_enum_union(&ident, None, &mut next);
508 if let Some(next) = next {
509 self.types.insert(ident, next);
510 }
511 }
512
513 self
514 }
515
516 #[doc = include_str!("../../tests/optimizer/complex_flatten.xsd")]
524 #[doc = include_str!("../../tests/optimizer/expected0/resolve_typedefs.rs")]
529 #[doc = include_str!("../../tests/optimizer/expected1/resolve_typedefs.rs")]
534 pub fn resolve_typedefs(mut self) -> Self {
536 tracing::debug!("resolve_typedefs");
537
538 let typedefs = get_typedefs!(self);
539
540 macro_rules! resolve_base {
541 ($base:expr) => {
542 match &mut $base {
543 Base::None => (),
544 Base::Extension(x) => *x = typedefs.resolve(x).clone(),
545 Base::Restriction(x) => *x = typedefs.resolve(x).clone(),
546 }
547 };
548 }
549
550 let mut replaced_references = HashMap::new();
551
552 for type_ in self.types.values_mut() {
553 match type_ {
554 Type::Reference(x) if x.is_single() => {
555 let new_type = typedefs.resolve(&x.type_).clone();
556 replaced_references
557 .entry(x.type_.clone())
558 .or_insert_with(|| new_type.clone());
559 x.type_ = new_type;
560 }
561 Type::Union(x) => {
562 resolve_base!(&mut x.base);
563
564 for x in &mut *x.types {
565 x.type_ = typedefs.resolve(&x.type_).clone();
566 }
567 }
568 Type::Dynamic(x) => {
569 x.type_ = x.type_.as_ref().map(|x| typedefs.resolve(x)).cloned();
570
571 for x in &mut x.derived_types {
572 *x = typedefs.resolve(x).clone();
573 }
574 }
575 Type::Enumeration(x) => {
576 resolve_base!(&mut x.base);
577
578 for x in &mut *x.variants {
579 if let Some(x) = &mut x.type_ {
580 *x = typedefs.resolve(x).clone();
581 }
582 }
583 }
584 Type::ComplexType(x) => {
585 resolve_base!(&mut x.base);
586
587 if let Some(ident) = &mut x.content {
588 *ident = typedefs.resolve(ident).clone();
589 }
590
591 for attrib in &mut *x.attributes {
592 attrib.type_ = typedefs.resolve(&attrib.type_).clone();
593 }
594 }
595 Type::All(x) | Type::Choice(x) | Type::Sequence(x) => {
596 for element in &mut *x.elements {
597 element.type_ = typedefs.resolve(&element.type_).clone();
598 }
599 }
600 _ => (),
601 }
602 }
603
604 for type_ in self.types.values_mut() {
605 let Type::Dynamic(ai) = type_ else {
606 continue;
607 };
608
609 for derived in &mut ai.derived_types {
610 if let Some(new_type) = replaced_references.get(derived) {
611 *derived = new_type.clone();
612 }
613 }
614 }
615
616 self
617 }
618
619 #[doc = include_str!("../../tests/optimizer/duplicate.xsd")]
639 #[doc = include_str!("../../tests/optimizer/expected0/remove_duplicates.rs")]
644 #[doc = include_str!("../../tests/optimizer/expected1/remove_duplicates.rs")]
649 pub fn remove_duplicates(mut self) -> Self {
651 use std::collections::hash_map::Entry;
652
653 struct Value<'a> {
654 type_: &'a Type,
655 types: &'a Types,
656 }
657
658 impl PartialEq for Value<'_> {
659 fn eq(&self, other: &Self) -> bool {
660 self.type_.type_eq(other.type_, self.types)
661 }
662 }
663
664 impl Eq for Value<'_> {}
665
666 impl Hash for Value<'_> {
667 fn hash<H: Hasher>(&self, state: &mut H) {
668 self.type_.type_hash(state, self.types);
669 }
670 }
671
672 tracing::debug!("remove_duplicates");
673
674 let mut changed = true;
675
676 while changed {
677 changed = false;
678
679 tracing::trace!("remove_duplicates new iteration");
680
681 let types = &self.types;
682
683 let mut map = HashMap::new();
684 let mut idents = HashMap::new();
685
686 for (ident, type_) in self.types.iter() {
687 match map.entry(Value { type_, types }) {
688 Entry::Vacant(e) => {
689 e.insert(ident.clone());
690 }
691 Entry::Occupied(e) => {
692 let reference_ident = e.get();
693 if !matches!(type_, Type::Reference(ti) if &ti.type_ == reference_ident) {
694 idents.insert(ident.clone(), reference_ident.clone());
695 }
696 }
697 }
698 }
699
700 if !idents.is_empty() {
701 changed = true;
702 self.typedefs = None;
703 }
704
705 for (ident, referenced_type) in idents {
706 println!("Create reference for duplicate type: {ident} => {referenced_type}");
707
708 self.types
709 .insert(ident, Type::Reference(ReferenceInfo::new(referenced_type)));
710 }
711 }
712
713 self
714 }
715
716 fn flatten_complex(
717 &self,
718 ident: &Ident,
719 min: MinOccurs,
720 max: MaxOccurs,
721 next: &mut FlattenComplexInfo,
722 ) {
723 let Some(type_) = self.types.get(ident) else {
724 return;
725 };
726
727 let mut is_choice = false;
728 let si = match type_ {
729 Type::Choice(si) => {
730 is_choice = true;
731 next.is_choice = true;
732
733 si
734 }
735 Type::All(si) | Type::Sequence(si) => si,
736 Type::Reference(ti) if ti.is_single() => {
737 self.flatten_complex(
738 &ti.type_,
739 min.min(ti.min_occurs),
740 max.max(ti.max_occurs),
741 next,
742 );
743
744 return;
745 }
746 x => crate::unreachable!("{x:#?}"),
747 };
748
749 next.count += 1;
750
751 for x in &*si.elements {
752 match x.element_mode {
753 ElementMode::Group => {
754 let (min, max) = if is_choice {
755 (min.min(x.min_occurs), max.max(x.max_occurs))
756 } else {
757 (min + x.min_occurs, max + x.max_occurs)
758 };
759
760 self.flatten_complex(&x.type_, min, max, next);
761 }
762 ElementMode::Element => {
763 let element = next
764 .info
765 .elements
766 .find_or_insert(x.ident.clone(), |_| x.clone());
767 element.min_occurs = min.min(x.min_occurs);
768 element.max_occurs = max.max(x.max_occurs);
769 }
770 }
771 }
772
773 if let Some(any) = &si.any {
774 next.info.any = Some(any.clone());
775 }
776 }
777
778 fn flatten_union(
779 &self,
780 ident: &Ident,
781 display_name: Option<&str>,
782 next: &mut FlattenUnionInfo,
783 ) {
784 let Some(type_) = self.types.get(ident) else {
785 return;
786 };
787
788 match type_ {
789 Type::Union(x) => {
790 next.count += 1;
791 for t in &*x.types {
792 self.flatten_union(&t.type_, t.display_name.as_deref(), next);
793 }
794 }
795 Type::Reference(x) if x.is_single() => {
796 self.flatten_union(&x.type_, display_name, next);
797 }
798 _ => {
799 let mut ui = UnionTypeInfo::new(ident.clone());
800 ui.display_name = display_name.map(ToOwned::to_owned);
801
802 next.info.types.push(ui);
803 }
804 }
805 }
806
807 fn flatten_enum_union(
808 &self,
809 ident: &Ident,
810 display_name: Option<&str>,
811 next: &mut Option<Type>,
812 ) {
813 let Some(type_) = self.types.get(ident) else {
814 return;
815 };
816
817 match type_ {
818 Type::Union(x) => {
819 for t in &*x.types {
820 self.flatten_enum_union(&t.type_, t.display_name.as_deref(), next);
821 }
822 }
823 Type::Enumeration(x) => {
824 *next = match next.take() {
825 None => Some(Type::Enumeration(EnumerationInfo::default())),
826 Some(Type::Enumeration(ei)) => Some(Type::Enumeration(ei)),
827 Some(Type::Union(ui)) => {
828 let mut ei = EnumerationInfo::default();
829
830 for t in ui.types.0 {
831 let var = ei.variants.find_or_insert(t.type_.clone(), |ident| {
832 VariantInfo::new(ident).with_type(Some(t.type_.clone()))
833 });
834 var.display_name = t.display_name;
835 }
836
837 Some(Type::Enumeration(ei))
838 }
839 _ => crate::unreachable!(),
840 };
841
842 let Some(Type::Enumeration(ei)) = next else {
843 crate::unreachable!();
844 };
845
846 for var in &*x.variants {
847 let new_var = ei.variants.find_or_insert(var.ident.clone(), |ident| {
848 VariantInfo::new(ident).with_type(var.type_.clone())
849 });
850 new_var.display_name.clone_from(&var.display_name);
851 }
852 }
853 Type::Reference(x) if x.is_single() => {
854 self.flatten_enum_union(&x.type_, display_name, next);
855 }
856 _ => {
857 if next.is_none() {
858 *next = Some(Type::Union(UnionInfo::default()));
859 }
860
861 match next {
862 Some(Type::Union(ui)) => {
863 let mut ti = UnionTypeInfo::new(ident.clone());
864 ti.display_name = display_name.map(ToOwned::to_owned);
865
866 ui.types.push(ti);
867 }
868 Some(Type::Enumeration(ei)) => {
869 let var = ei.variants.find_or_insert(ident.clone(), |x| {
870 VariantInfo::new(x).with_type(Some(ident.clone()))
871 });
872 var.display_name = display_name.map(ToOwned::to_owned);
873 }
874 _ => crate::unreachable!(),
875 }
876 }
877 }
878 }
879}
880
881impl BaseMap {
882 fn new(types: &Types) -> Self {
883 let mut ret = HashMap::new();
884
885 for (ident, type_) in &types.types {
886 match type_ {
887 Type::Enumeration(ei) => {
888 if matches!(
889 ei.base.as_ident().and_then(|base| types.get(base)),
890 Some(Type::Enumeration(_))
891 ) {
892 ret.insert(ident.clone(), ei.base.clone());
893 }
894 }
895 Type::Union(ei) => {
896 if matches!(
897 ei.base.as_ident().and_then(|base| types.get(base)),
898 Some(Type::Union(_))
899 ) {
900 ret.insert(ident.clone(), ei.base.clone());
901 }
902 }
903 Type::ComplexType(ci) => {
904 if matches!(
905 ci.base.as_ident().and_then(|base| types.get(base)),
906 Some(Type::ComplexType(_))
907 ) {
908 ret.insert(ident.clone(), ci.base.clone());
909 }
910 }
911 _ => (),
912 }
913 }
914
915 Self(ret)
916 }
917
918 fn get_unrestricted<'a>(&'a self, ident: &'a Ident) -> &'a Ident {
919 match self.0.get(ident) {
920 Some(Base::Restriction(base)) => self.get_unrestricted(base),
921 _ => ident,
922 }
923 }
924}
925
926impl TypedefMap {
927 fn new(types: &Types) -> Self {
928 let mut ret = HashMap::new();
929
930 for (ident, type_) in &types.types {
931 if let Type::Reference(x) = type_ {
932 if x.is_single() {
933 ret.insert(ident.clone(), x.type_.clone());
934 }
935 }
936 }
937
938 Self(ret)
939 }
940
941 fn resolve<'a>(&'a self, ident: &'a Ident) -> &'a Ident {
942 let x = self.0.get(ident).map_or(ident, |x| self.resolve(x));
943
944 x
945 }
946}