facet_diff/
display.rs

1use std::fmt::{Display, Write};
2
3use facet::TypeNameOpts;
4use facet_pretty::PrettyPrinter;
5
6use crate::{
7    diff::{Diff, Value},
8    sequences::{ReplaceGroup, Updates, UpdatesGroup},
9};
10
11struct PadAdapter<'a, 'b: 'a> {
12    fmt: &'a mut std::fmt::Formatter<'b>,
13    on_newline: bool,
14}
15
16impl<'a, 'b> Write for PadAdapter<'a, 'b> {
17    fn write_str(&mut self, s: &str) -> std::fmt::Result {
18        for line in s.split_inclusive('\n') {
19            if self.on_newline {
20                self.fmt.write_str("    ")?;
21            }
22
23            self.on_newline = line.ends_with('\n');
24
25            self.fmt.write_str(line)?;
26        }
27
28        Ok(())
29    }
30
31    fn write_char(&mut self, c: char) -> std::fmt::Result {
32        if self.on_newline {
33            self.fmt.write_str("    ")?;
34        }
35
36        self.on_newline = c == '\n';
37        self.fmt.write_char(c)
38    }
39}
40
41impl<'mem, 'facet> Display for Diff<'mem, 'facet> {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        match self {
44            Diff::Equal => f.write_str("equal"),
45            Diff::Replace { from, to } => {
46                let printer = PrettyPrinter::default().with_colors(false);
47
48                if from.shape().id != to.shape().id {
49                    f.write_str("\x1b[1m")?;
50                    from.type_name(f, TypeNameOpts::infinite())?;
51                    f.write_str("\x1b[m => \x1b[1m")?;
52                    to.type_name(f, TypeNameOpts::infinite())?;
53                    f.write_str(" \x1b[m")?;
54                }
55
56                f.write_str("{\n\x1b[31m")?; // Print the next value in red
57                //
58                let mut indent = PadAdapter {
59                    fmt: f,
60                    on_newline: true,
61                };
62
63                writeln!(indent, "{}\x1b[32m", printer.format_peek(*from))?;
64                write!(indent, "{}", printer.format_peek(*to))?;
65                f.write_str("\n\x1b[m}") // Reset the colors
66            }
67            Diff::User {
68                from,
69                to,
70                variant,
71                value,
72            } => {
73                let printer = PrettyPrinter::default().with_colors(false);
74                let mut indent = PadAdapter {
75                    fmt: f,
76                    on_newline: false,
77                };
78
79                write!(indent, "\x1b[1m")?;
80                from.write_type_name(indent.fmt, TypeNameOpts::infinite())?;
81
82                if let Some(variant) = variant {
83                    write!(indent, "\x1b[m::\x1b[1m{variant}")?;
84                }
85
86                if from.id != to.id {
87                    write!(indent, "\x1b[m => \x1b[1m")?;
88                    to.write_type_name(indent.fmt, TypeNameOpts::infinite())?;
89
90                    if let Some(variant) = variant {
91                        write!(indent, "\x1b[m::\x1b[1m{variant}")?;
92                    }
93                }
94
95                match value {
96                    Value::Struct {
97                        updates,
98                        deletions,
99                        insertions,
100                        unchanged: _,
101                    } => {
102                        writeln!(indent, "\x1b[m {{")?;
103                        for (field, update) in updates {
104                            writeln!(indent, "{field}: {update}")?;
105                        }
106
107                        for (field, value) in deletions {
108                            writeln!(
109                                indent,
110                                "\x1b[31m{field}: {}\x1b[m",
111                                printer.format_peek(*value)
112                            )?;
113                        }
114
115                        for (field, value) in insertions {
116                            writeln!(
117                                indent,
118                                "\x1b[32m{field}: {}\x1b[m",
119                                printer.format_peek(*value)
120                            )?;
121                        }
122
123                        f.write_str("}")
124                    }
125                    Value::Tuple { updates } => {
126                        writeln!(indent, "\x1b[m (")?;
127                        write!(indent, "{updates}")?;
128                        f.write_str(")")
129                    }
130                }
131            }
132            Diff::Sequence { from, to, updates } => {
133                write!(f, "\x1b[1m")?;
134                from.write_type_name(f, TypeNameOpts::infinite())?;
135                write!(f, "\x1b[m")?;
136
137                if from.id != to.id {
138                    write!(f, " => \x1b[1m")?;
139                    to.write_type_name(f, TypeNameOpts::infinite())?;
140                    write!(f, "\x1b[m")?;
141                }
142
143                let mut indent = PadAdapter {
144                    fmt: f,
145                    on_newline: false,
146                };
147
148                writeln!(indent, " [")?;
149                write!(indent, "{updates}")?;
150                write!(f, "]")
151            }
152        }
153    }
154}
155
156impl<'mem, 'facet> Display for Updates<'mem, 'facet> {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        if let Some(update) = &self.0.first {
159            update.fmt(f)?;
160        }
161
162        let printer = PrettyPrinter::default().with_colors(false);
163
164        for (values, update) in &self.0.values {
165            for value in values {
166                writeln!(f, "{}", printer.format_peek(*value))?;
167            }
168            update.fmt(f)?;
169        }
170
171        if let Some(values) = &self.0.last {
172            for value in values {
173                writeln!(f, "{}", printer.format_peek(*value))?;
174            }
175        }
176
177        Ok(())
178    }
179}
180
181impl<'mem, 'facet> Display for UpdatesGroup<'mem, 'facet> {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        if let Some(update) = &self.0.first {
184            update.fmt(f)?;
185        }
186
187        for (values, update) in &self.0.values {
188            for value in values {
189                writeln!(f, "{value}")?;
190            }
191            update.fmt(f)?;
192        }
193
194        if let Some(values) = &self.0.last {
195            for value in values {
196                writeln!(f, "{value}")?;
197            }
198        }
199
200        Ok(())
201    }
202}
203
204impl<'mem, 'facet> Display for ReplaceGroup<'mem, 'facet> {
205    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206        let printer = PrettyPrinter::default().with_colors(false);
207
208        for remove in &self.removals {
209            writeln!(f, "\x1b[31m{}\x1b[m", printer.format_peek(*remove))?;
210        }
211
212        for add in &self.additions {
213            writeln!(f, "\x1b[32m{}\x1b[m", printer.format_peek(*add))?;
214        }
215
216        Ok(())
217    }
218}