shapely_core/shape/
pretty_print.rs

1use super::{FieldFlags, Innards, NameOpts, Shape, VariantKind};
2use std::{collections::HashSet, fmt::Formatter};
3
4impl Shape {
5    /// Pretty-print this shape, recursively.
6    pub fn pretty_print_recursive(&self, f: &mut Formatter) -> std::fmt::Result {
7        self.pretty_print_recursive_internal(f, &mut HashSet::new(), 0)
8    }
9
10    fn pretty_print_recursive_internal(
11        &self,
12        f: &mut Formatter,
13        printed_schemas: &mut HashSet<Shape>,
14        indent: usize,
15    ) -> std::fmt::Result {
16        if !printed_schemas.insert(*self) {
17            write!(f, "{:indent$}\x1b[1;33m", "", indent = indent)?;
18            (self.name)(f, NameOpts::one())?;
19            writeln!(f, "\x1b[0m (\x1b[1;31malready printed\x1b[0m)")?;
20            return Ok(());
21        }
22
23        write!(f, "{:indent$}\x1b[1;33m", "", indent = indent)?;
24        (self.name)(f, NameOpts::default())?;
25        writeln!(f, "\x1b[0m (\x1b[1;34m{}\x1b[0m bytes)", self.layout.size())?;
26
27        match &self.innards {
28            Innards::Struct { fields }
29            | Innards::TupleStruct { fields }
30            | Innards::Tuple { fields } => {
31                let max_name_length = fields.iter().map(|f| f.name.len()).max().unwrap_or(0);
32                for field in *fields {
33                    write!(
34                        f,
35                        "{:indent$}\x1b[1;34m{:>4}\x1b[0m \x1b[1;32m{:<width$}\x1b[0m ",
36                        "",
37                        field.offset,
38                        field.name,
39                        indent = indent + Self::INDENT,
40                        width = max_name_length
41                    )?;
42                    if field.flags.contains(FieldFlags::SENSITIVE) {
43                        write!(f, "(sensitive) ")?;
44                    }
45                    if let Innards::Scalar(_) = field.shape.get().innards {
46                        field.shape.get().pretty_print_recursive_internal(
47                            f,
48                            printed_schemas,
49                            indent + Self::INDENT * 2,
50                        )?;
51                    } else {
52                        writeln!(f)?;
53                        field.shape.get().pretty_print_recursive_internal(
54                            f,
55                            printed_schemas,
56                            indent + Self::INDENT * 2,
57                        )?;
58                    }
59                }
60            }
61            Innards::Map { value_shape, .. } => {
62                writeln!(
63                    f,
64                    "{:indent$}\x1b[1;36mHashMap with arbitrary keys and value shape:\x1b[0m",
65                    "",
66                    indent = indent + Self::INDENT
67                )?;
68                value_shape.get().pretty_print_recursive_internal(
69                    f,
70                    printed_schemas,
71                    indent + Self::INDENT * 2,
72                )?;
73            }
74            Innards::List { item_shape, .. } => {
75                write!(
76                    f,
77                    "{:indent$}\x1b[1;36mArray of:\x1b[0m ",
78                    "",
79                    indent = indent + Self::INDENT
80                )?;
81                item_shape.get().pretty_print_recursive_internal(
82                    f,
83                    printed_schemas,
84                    indent + Self::INDENT * 2,
85                )?;
86            }
87            Innards::Transparent(inner_schema) => {
88                write!(
89                    f,
90                    "{:indent$}\x1b[1;36mTransparent wrapper for:\x1b[0m ",
91                    "",
92                    indent = indent + Self::INDENT
93                )?;
94                inner_schema.get().pretty_print_recursive_internal(
95                    f,
96                    printed_schemas,
97                    indent + Self::INDENT * 2,
98                )?;
99            }
100            Innards::Scalar(_scalar) => {
101                // let's not duplicate `u64 => U64` for example
102            }
103            Innards::Enum { variants, repr: _ } => {
104                writeln!(
105                    f,
106                    "{:indent$}\x1b[1;36mEnum with {} variants:\x1b[0m",
107                    "",
108                    variants.len(),
109                    indent = indent + Self::INDENT
110                )?;
111
112                for variant in *variants {
113                    match &variant.kind {
114                        VariantKind::Unit => {
115                            let disc_str = if let Some(disc) = variant.discriminant {
116                                format!(" = {}", disc)
117                            } else {
118                                String::new()
119                            };
120                            writeln!(
121                                f,
122                                "{:indent$}\x1b[1;32m{}{}\x1b[0m",
123                                "",
124                                variant.name,
125                                disc_str,
126                                indent = indent + Self::INDENT * 2
127                            )?;
128                        }
129                        VariantKind::Tuple { fields } => {
130                            writeln!(
131                                f,
132                                "{:indent$}\x1b[1;32m{}\x1b[0m({})",
133                                "",
134                                variant.name,
135                                (0..fields.len())
136                                    .map(|_| "_")
137                                    .collect::<Vec<_>>()
138                                    .join(", "),
139                                indent = indent + Self::INDENT * 2
140                            )?;
141
142                            // Print field details
143                            for field in *fields {
144                                write!(
145                                    f,
146                                    "{:indent$}\x1b[1;34m{:>4}\x1b[0m \x1b[1;32m{}\x1b[0m ",
147                                    "",
148                                    field.offset,
149                                    field.name,
150                                    indent = indent + Self::INDENT * 3
151                                )?;
152                                if field.flags.contains(FieldFlags::SENSITIVE) {
153                                    write!(f, "(sensitive) ")?;
154                                }
155                                if let Innards::Scalar(_) = field.shape.get().innards {
156                                    field.shape.get().pretty_print_recursive_internal(
157                                        f,
158                                        printed_schemas,
159                                        indent + Self::INDENT * 4,
160                                    )?;
161                                } else {
162                                    writeln!(f)?;
163                                    field.shape.get().pretty_print_recursive_internal(
164                                        f,
165                                        printed_schemas,
166                                        indent + Self::INDENT * 4,
167                                    )?;
168                                }
169                            }
170                        }
171                        VariantKind::Struct { fields } => {
172                            writeln!(
173                                f,
174                                "{:indent$}\x1b[1;32m{}\x1b[0m {{ {} }}",
175                                "",
176                                variant.name,
177                                fields.iter().map(|f| f.name).collect::<Vec<_>>().join(", "),
178                                indent = indent + Self::INDENT * 2
179                            )?;
180
181                            // Print field details
182                            for field in *fields {
183                                write!(
184                                    f,
185                                    "{:indent$}\x1b[1;34m{:>4}\x1b[0m \x1b[1;32m{}\x1b[0m ",
186                                    "",
187                                    field.offset,
188                                    field.name,
189                                    indent = indent + Self::INDENT * 3
190                                )?;
191                                if field.flags.contains(FieldFlags::SENSITIVE) {
192                                    write!(f, "(sensitive) ")?;
193                                }
194                                if let Innards::Scalar(_) = field.shape.get().innards {
195                                    field.shape.get().pretty_print_recursive_internal(
196                                        f,
197                                        printed_schemas,
198                                        indent + Self::INDENT * 4,
199                                    )?;
200                                } else {
201                                    writeln!(f)?;
202                                    field.shape.get().pretty_print_recursive_internal(
203                                        f,
204                                        printed_schemas,
205                                        indent + Self::INDENT * 4,
206                                    )?;
207                                }
208                            }
209                        }
210                    }
211                }
212            }
213        }
214
215        Ok(())
216    }
217}