xsd_parser/optimizer/
remove_duplicates.rs1use std::collections::HashMap;
2use std::hash::{Hash, Hasher};
3
4use crate::types::{ReferenceInfo, Type, TypeEq, TypeVariant, Types};
5
6use super::Optimizer;
7
8impl Optimizer {
9 #[doc = include_str!("../../tests/optimizer/duplicate.xsd")]
29 #[doc = include_str!("../../tests/optimizer/expected0/remove_duplicates.rs")]
34 #[doc = include_str!("../../tests/optimizer/expected1/remove_duplicates.rs")]
39 pub fn remove_duplicates(mut self) -> Self {
41 use std::collections::hash_map::Entry;
42
43 struct Value<'a> {
44 type_: &'a Type,
45 types: &'a Types,
46 }
47
48 impl PartialEq for Value<'_> {
49 fn eq(&self, other: &Self) -> bool {
50 self.type_.type_eq(other.type_, self.types)
51 }
52 }
53
54 impl Eq for Value<'_> {}
55
56 impl Hash for Value<'_> {
57 fn hash<H: Hasher>(&self, state: &mut H) {
58 self.type_.type_hash(state, self.types);
59 }
60 }
61
62 tracing::debug!("remove_duplicates");
63
64 let mut changed = true;
65
66 while changed {
67 changed = false;
68
69 tracing::trace!("remove_duplicates new iteration");
70
71 let types = &self.types;
72
73 let mut map = HashMap::new();
74 let mut idents = HashMap::new();
75
76 for (ident, type_) in self.types.iter() {
77 match map.entry(Value { type_, types }) {
78 Entry::Vacant(e) => {
79 if let Some(ident) = types.get_resolved_ident(ident) {
80 e.insert(ident.clone());
81 }
82 }
83 Entry::Occupied(e) => {
84 let reference_ident = e.get();
85 if !matches!(&type_.variant, TypeVariant::Reference(ti) if &ti.type_ == reference_ident)
86 {
87 idents.insert(ident.clone(), reference_ident.clone());
88 }
89 }
90 }
91 }
92
93 if !idents.is_empty() {
94 changed = true;
95 self.typedefs = None;
96 }
97
98 for (ident, referenced_type) in idents {
99 tracing::trace!(
100 "Create reference for duplicate type: {ident} => {referenced_type}"
101 );
102
103 let ty = self.types.get_mut(&ident).unwrap();
104 ty.variant = TypeVariant::Reference(ReferenceInfo::new(referenced_type));
105 }
106 }
107
108 self
109 }
110}