xsd_parser/
meta_types_printer.rs

1use std::collections::HashSet;
2use std::fmt::{Display, Formatter, Result as FmtResult};
3
4use crate::models::{
5    meta::{
6        AttributeMetaVariant, ElementMetaVariant, ElementMode, MetaType, MetaTypeVariant, MetaTypes,
7    },
8    Ident,
9};
10
11/// Pretty-printer for [`MetaTypes`] content.
12///
13/// The [`MetaTypesPrinter`] recursively formats all known [`MetaType`] entries
14/// in a structured and indented way. It handles all variant types
15/// (`BuildIn`, `Custom`, `Reference`, `ComplexType`, etc.) and also detects
16/// and prevents cycles by tracking visited type identifiers.
17///
18/// This type is typically used via its [`Display`] implementation.
19#[derive(Debug)]
20pub struct MetaTypesPrinter<'a> {
21    types: &'a MetaTypes,
22}
23
24#[derive(Default)]
25struct State {
26    level: usize,
27    visit: HashSet<Ident>,
28}
29
30impl<'a> MetaTypesPrinter<'a> {
31    /// Create a new [`MetaTypesPrinter`] from the passed `types`.
32    #[must_use]
33    pub fn new(types: &'a MetaTypes) -> Self {
34        Self { types }
35    }
36
37    /// Print the information of all meta types to the passed formatter `f`.
38    ///
39    /// # Errors
40    ///
41    /// Forwards the error raised by the formatter.
42    pub fn print_all(&self, f: &mut Formatter<'_>) -> FmtResult {
43        let mut s = State::default();
44
45        for (ident, ty) in &self.types.items {
46            self.print_type_impl(f, &mut s, ident, ty)?;
47        }
48
49        Ok(())
50    }
51
52    /// Print the information of a single meta type identified by `ident` to
53    /// the passed formatter `f`.
54    ///
55    /// # Errors
56    ///
57    /// Forwards the error raised by the formatter.
58    pub fn print_type(&self, ident: &Ident, f: &mut Formatter<'_>) -> FmtResult {
59        let mut s = State::default();
60
61        if let Some(ty) = self.types.items.get(ident) {
62            self.print_type_impl(f, &mut s, ident, ty)?;
63        }
64
65        Ok(())
66    }
67
68    fn resolve_complex_type(
69        &self,
70        f: &mut Formatter<'_>,
71        s: &mut State,
72        ident: &Ident,
73    ) -> FmtResult {
74        if let Some(x) = self.types.items.get(ident) {
75            self.print_type_impl(f, s, ident, x)
76        } else {
77            writeln!(f, "NOT FOUND")?;
78
79            Ok(())
80        }
81    }
82
83    #[allow(clippy::too_many_lines)]
84    fn print_type_impl(
85        &self,
86        f: &mut Formatter<'_>,
87        s: &mut State,
88        ident: &Ident,
89        ty: &MetaType,
90    ) -> FmtResult {
91        macro_rules! indent {
92            ($( $tt:tt )*) => {{
93                write!(f, "{0:1$}", "", 4 * s.level)?;
94                write!(f, $( $tt )*)?;
95            }};
96        }
97
98        macro_rules! indentln {
99            ($( $tt:tt )*) => {{
100                write!(f, "{0:1$}", "", 4 * s.level)?;
101                writeln!(f, $( $tt )*)?;
102            }};
103        }
104
105        macro_rules! write_constrains {
106            ($c:expr) => {{
107                let x = $c;
108
109                s.level += 1;
110
111                indentln!("range={:?}", x.range);
112                indentln!("total_digits={:?}", x.total_digits);
113                indentln!("fraction_digits={:?}", x.fraction_digits);
114                indentln!("patterns={:?}", x.patterns);
115                indentln!("min_length={:?}", x.min_length);
116                indentln!("max_length={:?}", x.max_length);
117                indentln!("whitespace={:?}", x.whitespace);
118
119                s.level -= 1;
120            }};
121        }
122
123        if !s.visit.insert(ident.clone()) {
124            writeln!(f, "LOOP DETECTED ({})", ident.name)?;
125
126            return Ok(());
127        }
128
129        match &ty.variant {
130            MetaTypeVariant::BuildIn(x) => {
131                writeln!(f, "{}: BuildIn", ident)?;
132
133                s.level += 1;
134
135                indentln!("display_name={:?}", &ty.display_name);
136                indentln!("type={x:?}");
137
138                s.level -= 1;
139            }
140            MetaTypeVariant::Custom(x) => {
141                writeln!(f, "{}: Custom", ident)?;
142
143                s.level += 1;
144
145                indentln!("display_name={:?}", &ty.display_name);
146                indentln!("type={x:?}");
147
148                s.level -= 1;
149            }
150            MetaTypeVariant::Union(x) => {
151                writeln!(f, "{}: Union", ident)?;
152
153                s.level += 1;
154
155                indentln!("display_name={:?}", &ty.display_name);
156                indentln!("base={}", x.base);
157                indentln!("constrains:");
158                write_constrains!(&x.constrains);
159                indentln!("types:");
160
161                s.level += 1;
162
163                for ty in &*x.types {
164                    indentln!("{}", &ty.type_);
165                }
166
167                s.level -= 2;
168            }
169            MetaTypeVariant::Reference(x) => {
170                writeln!(f, "{}: Reference", ident)?;
171
172                s.level += 1;
173
174                indentln!("display_name={:?}", &ty.display_name);
175                indentln!("min={}", x.min_occurs);
176                indentln!("max={:?}", x.max_occurs);
177                indentln!("nillable={:?}", x.nillable);
178                indentln!("type={}", x.type_);
179
180                s.level -= 1;
181            }
182            MetaTypeVariant::Dynamic(x) => {
183                writeln!(f, "{}: Dynamic", ident)?;
184
185                s.level += 1;
186
187                indentln!("display_name={:?}", &ty.display_name);
188                indentln!(
189                    "type={}",
190                    x.type_
191                        .as_ref()
192                        .map_or_else(|| String::from("None"), ToString::to_string)
193                );
194                indentln!("derived_types:");
195
196                s.level += 1;
197
198                for ty in &*x.derived_types {
199                    indentln!("{}", ty);
200                }
201
202                s.level -= 2;
203            }
204            MetaTypeVariant::Enumeration(x) => {
205                writeln!(f, "{}: Enumeration", ident)?;
206
207                s.level += 1;
208
209                indentln!("display_name={:?}", &ty.display_name);
210                indentln!("base={}", x.base);
211                indentln!("constrains:");
212                write_constrains!(&x.constrains);
213                indentln!("variants:");
214
215                s.level += 1;
216
217                for var in &*x.variants {
218                    indentln!("{}={:?}", var.ident.name, var.use_);
219                }
220
221                s.level -= 2;
222            }
223            MetaTypeVariant::All(x) | MetaTypeVariant::Choice(x) | MetaTypeVariant::Sequence(x) => {
224                match &ty.variant {
225                    MetaTypeVariant::All(_) => writeln!(f, "{}: All", ident)?,
226                    MetaTypeVariant::Choice(_) => writeln!(f, "{}: Choice", ident)?,
227                    MetaTypeVariant::Sequence(_) => writeln!(f, "{}: Sequence", ident)?,
228                    _ => (),
229                }
230
231                s.level += 1;
232
233                indentln!("display_name={:?}", &ty.display_name);
234                indentln!("is_mixed={:?}", x.is_mixed);
235
236                for x in &*x.elements {
237                    indentln!("element:");
238
239                    s.level += 1;
240
241                    indentln!("name={}", x.ident.name);
242                    indentln!("form={:?}", x.form);
243                    indentln!("nillable={:?}", x.nillable);
244                    indentln!("min_occurs={}", x.min_occurs);
245                    indentln!("max_occurs={:?}", x.max_occurs);
246
247                    match &x.variant {
248                        ElementMetaVariant::Text => {
249                            indentln!("variant=Text");
250                        }
251                        ElementMetaVariant::Type {
252                            type_,
253                            mode: ElementMode::Element,
254                        } => {
255                            indentln!("variant=Type({})", type_);
256                        }
257                        ElementMetaVariant::Type {
258                            type_,
259                            mode: ElementMode::Group,
260                        } => {
261                            indent!("variant=");
262                            self.resolve_complex_type(f, s, type_)?;
263                        }
264                        ElementMetaVariant::Any { meta: x } => {
265                            indentln!("variant=Any");
266
267                            s.level += 1;
268
269                            if let Some(x) = &x.id {
270                                indentln!("id={x:?}");
271                            }
272                            if let Some(x) = &x.namespace {
273                                indentln!("namespace={x:?}");
274                            }
275                            if let Some(x) = &x.not_q_name {
276                                indentln!("not_q_name={x:?}");
277                            }
278                            if let Some(x) = &x.not_namespace {
279                                indentln!("not_namespace={x:?}");
280                            }
281
282                            indentln!("process_contents={:?}", x.process_contents);
283
284                            s.level -= 1;
285                        }
286                    }
287
288                    s.level -= 1;
289                }
290
291                s.level -= 1;
292            }
293            MetaTypeVariant::ComplexType(x) => {
294                writeln!(f, "{}: ComplexType", ident)?;
295
296                s.level += 1;
297
298                indentln!("display_name={:?}", &ty.display_name);
299                indentln!("base={}", x.base);
300                indentln!("min_occurs={}", x.min_occurs);
301                indentln!("max_occurs={:?}", x.max_occurs);
302                indentln!("is_dynamic={}", x.is_dynamic);
303                indentln!("is_mixed={:?}", x.is_mixed);
304
305                for x in &*x.attributes {
306                    indentln!("attribute:");
307
308                    s.level += 1;
309
310                    indentln!("name={}", x.ident.name);
311                    indentln!("use={:?}", x.use_);
312                    indentln!("form={:?}", x.form);
313                    indentln!("default={:?}", x.default);
314
315                    match &x.variant {
316                        AttributeMetaVariant::Type(type_) => indentln!("type=Type({})", type_),
317                        AttributeMetaVariant::Any(x) => {
318                            indentln!("type=Any");
319
320                            s.level += 1;
321
322                            if let Some(x) = &x.id {
323                                indentln!("id={x:?}");
324                            }
325                            if let Some(x) = &x.namespace {
326                                indentln!("namespace={x:?}");
327                            }
328                            if let Some(x) = &x.not_q_name {
329                                indentln!("not_q_name={x:?}");
330                            }
331                            if let Some(x) = &x.not_namespace {
332                                indentln!("not_namespace={x:?}");
333                            }
334
335                            indentln!("process_contents={:?}", x.process_contents);
336
337                            s.level -= 1;
338                        }
339                    }
340
341                    s.level -= 1;
342                }
343
344                if let Some(content) = &x.content {
345                    indent!("content=");
346                    self.resolve_complex_type(f, s, content)?;
347                }
348
349                s.level -= 1;
350            }
351            MetaTypeVariant::SimpleType(x) => {
352                writeln!(f, "{}: SimpleType", ident)?;
353
354                s.level += 1;
355
356                indentln!("base={}", x.base);
357                indentln!("is_list={}", x.is_list);
358                indentln!("constrains:");
359                write_constrains!(&x.constrains);
360
361                s.level -= 1;
362            }
363        }
364
365        s.visit.remove(ident);
366
367        Ok(())
368    }
369}
370
371impl Display for MetaTypesPrinter<'_> {
372    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
373        self.print_all(f)
374    }
375}