1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use crate::models::meta::{MetaType, MetaTypeVariant, MetaTypes, ReferenceMeta, TypeEq};
use super::Optimizer;
impl Optimizer {
/// If two types are completely equal this optimization will generate the
/// first type complete and just a type definition for the second one.
///
/// <div class="warning">
/// *Caution*
///
/// Be careful with this optimization. This will compare each known
/// type with each other type and check if the types are identical or not.
/// This would result in a type reference for two types, even if the types
/// itself are not logically related to each other.
///
/// Furthermore this may result in typedef loops. The code generator should
/// be able to deal with them (using a Box), but it is still risky to use it.
/// </div>
///
/// # Examples
///
/// Consider the following XML schema.
/// ```xml
#[doc = include_str!("../../../tests/optimizer/duplicate.xsd")]
/// ```
///
/// Without this optimization this will result in the following code:
/// ```rust
#[doc = include_str!("../../../tests/optimizer/expected0/remove_duplicates.rs")]
/// ```
///
/// With this optimization the following code is generated:
/// ```rust
#[doc = include_str!("../../../tests/optimizer/expected1/remove_duplicates.rs")]
/// ```
pub fn remove_duplicates(mut self) -> Self {
use std::collections::hash_map::Entry;
struct Value<'a> {
type_: &'a MetaType,
types: &'a MetaTypes,
}
impl Debug for Value<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Value").field("type_", &self.type_).finish()
}
}
impl PartialEq for Value<'_> {
fn eq(&self, other: &Self) -> bool {
self.type_.type_eq(other.type_, self.types)
}
}
impl Eq for Value<'_> {}
impl Hash for Value<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.type_.type_hash(state, self.types);
}
}
tracing::debug!("remove_duplicates");
let mut changed = true;
while changed {
changed = false;
tracing::trace!("remove_duplicates new iteration");
let types = &self.types;
#[allow(clippy::mutable_key_type)]
let mut map = HashMap::new();
let mut idents = HashMap::new();
for (ident, type_) in &self.types.items {
match map.entry(Value { type_, types }) {
Entry::Vacant(e) => {
if let Some(ident) = types.get_resolved_ident(ident) {
e.insert(ident.clone());
}
}
Entry::Occupied(e) => {
let reference_ident = e.get();
if !matches!(&type_.variant, MetaTypeVariant::Reference(ti) if ti.type_.eq(reference_ident))
{
idents.insert(ident.clone(), reference_ident.clone());
}
}
}
}
if !idents.is_empty() {
changed = true;
self.typedefs = None;
}
for (ident, referenced_type) in idents {
tracing::trace!(
"Create reference for duplicate type: {ident} => {referenced_type}"
);
let ty = self.types.items.get_mut(&ident).unwrap();
ty.variant = MetaTypeVariant::Reference(ReferenceMeta::new(referenced_type));
}
}
self
}
}