• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 20074067475

09 Dec 2025 06:20PM UTC coverage: 58.542% (+0.2%) from 58.373%
20074067475

Pull #1212

github

web-flow
Merge 57aecfa87 into 7cadf0be6
Pull Request #1212: feat(facet-core): add #[facet(metadata = kind)] field attribute

362 of 491 new or added lines in 7 files covered. (73.73%)

15 existing lines in 2 files now uncovered.

26683 of 45579 relevant lines covered (58.54%)

623.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.04
/facet-diff/src/display.rs
1
use std::fmt::{Display, Write};
2

3
use facet_pretty::{PrettyPrinter, tokyo_night};
4
use owo_colors::OwoColorize;
5

6
use crate::{
7
    diff::{Diff, Value},
8
    sequences::{ReplaceGroup, Updates, UpdatesGroup},
9
};
10

11
/// Format text for deletions
12
fn deleted(s: &str) -> String {
121✔
13
    format!("{}", s.color(tokyo_night::DELETION))
121✔
14
}
121✔
15

16
/// Format text for insertions
17
fn inserted(s: &str) -> String {
124✔
18
    format!("{}", s.color(tokyo_night::INSERTION))
124✔
19
}
124✔
20

21
/// Format muted text (unchanged indicators, structural equality)
22
fn muted(s: &str) -> String {
112✔
23
    format!("{}", s.color(tokyo_night::MUTED))
112✔
24
}
112✔
25

26
/// Format field name
27
fn field(s: &str) -> String {
73✔
28
    format!("{}", s.color(tokyo_night::FIELD_NAME))
73✔
29
}
73✔
30

31
/// Format punctuation as dimmed
32
fn punct(s: &str) -> String {
235✔
33
    format!("{}", s.color(tokyo_night::COMMENT))
235✔
34
}
235✔
35

36
struct PadAdapter<'a, 'b: 'a> {
37
    fmt: &'a mut std::fmt::Formatter<'b>,
38
    on_newline: bool,
39
    indent: &'static str,
40
}
41

42
impl<'a, 'b> PadAdapter<'a, 'b> {
43
    fn new_indented(fmt: &'a mut std::fmt::Formatter<'b>) -> Self {
85✔
44
        Self {
85✔
45
            fmt,
85✔
46
            on_newline: true,
85✔
47
            indent: "  ",
85✔
48
        }
85✔
49
    }
85✔
50
}
51

52
impl<'a, 'b> Write for PadAdapter<'a, 'b> {
53
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
1,442✔
54
        for line in s.split_inclusive('\n') {
1,442✔
55
            if self.on_newline {
1,442✔
56
                self.fmt.write_str(self.indent)?;
334✔
57
            }
1,108✔
58

59
            self.on_newline = line.ends_with('\n');
1,442✔
60

61
            self.fmt.write_str(line)?;
1,442✔
62
        }
63

64
        Ok(())
1,442✔
65
    }
1,442✔
66

UNCOV
67
    fn write_char(&mut self, c: char) -> std::fmt::Result {
×
68
        if self.on_newline {
×
69
            self.fmt.write_str(self.indent)?;
×
70
        }
×
71

UNCOV
72
        self.on_newline = c == '\n';
×
73
        self.fmt.write_char(c)
×
74
    }
×
75
}
76

77
impl<'mem, 'facet> Display for Diff<'mem, 'facet> {
78
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239✔
79
        match self {
239✔
80
            Diff::Equal { value: _ } => {
81
                write!(f, "{}", muted("(structurally equal)"))
25✔
82
            }
83
            Diff::Replace { from, to } => {
103✔
84
                let printer = PrettyPrinter::default()
103✔
85
                    .with_colors(false)
103✔
86
                    .with_minimal_option_names(true);
103✔
87

88
                // Show value change inline: old → new
89
                write!(
103✔
90
                    f,
103✔
91
                    "{} → {}",
92
                    deleted(&printer.format_peek(*from)),
103✔
93
                    inserted(&printer.format_peek(*to))
103✔
94
                )
95
            }
96
            Diff::User {
97
                from: _,
98
                to: _,
99
                variant,
77✔
100
                value,
77✔
101
            } => {
102
                let printer = PrettyPrinter::default()
77✔
103
                    .with_colors(false)
77✔
104
                    .with_minimal_option_names(true);
77✔
105

106
                // Show variant if present (e.g., "Some" for Option::Some)
107
                if let Some(variant) = variant {
77✔
108
                    write!(f, "{}", variant.bold())?;
12✔
109
                }
65✔
110

111
                let has_prefix = variant.is_some();
77✔
112

113
                match value {
77✔
114
                    Value::Struct {
115
                        updates,
61✔
116
                        deletions,
61✔
117
                        insertions,
61✔
118
                        unchanged,
61✔
119
                    } => {
120
                        if updates.is_empty() && deletions.is_empty() && insertions.is_empty() {
61✔
121
                            return write!(f, "{}", muted("(structurally equal)"));
9✔
122
                        }
52✔
123

124
                        if has_prefix {
52✔
125
                            writeln!(f, " {}", punct("{"))?;
1✔
126
                        } else {
127
                            writeln!(f, "{}", punct("{"))?;
51✔
128
                        }
129
                        let mut indent = PadAdapter::new_indented(f);
52✔
130

131
                        // Show unchanged fields indicator first
132
                        let unchanged_count = unchanged.len();
52✔
133
                        if unchanged_count > 0 {
52✔
134
                            let label = if unchanged_count == 1 {
30✔
135
                                "field"
21✔
136
                            } else {
137
                                "fields"
9✔
138
                            };
139
                            writeln!(
30✔
140
                                indent,
30✔
141
                                "{}",
142
                                muted(&format!(".. {unchanged_count} unchanged {label}"))
30✔
UNCOV
143
                            )?;
×
144
                        }
22✔
145

146
                        // Sort fields for deterministic output
147
                        let mut updates: Vec<_> = updates.iter().collect();
52✔
148
                        updates.sort_by(|(a, _), (b, _)| a.cmp(b));
52✔
149
                        for (fld, update) in updates {
69✔
150
                            writeln!(indent, "{}{} {update}", field(fld), punct(":"))?;
69✔
151
                        }
152

153
                        let mut deletions: Vec<_> = deletions.iter().collect();
52✔
154
                        deletions.sort_by(|(a, _), (b, _)| a.cmp(b));
52✔
155
                        for (fld, value) in deletions {
52✔
156
                            writeln!(
2✔
157
                                indent,
2✔
158
                                "{} {}{} {}",
159
                                deleted("-"),
2✔
160
                                field(fld),
2✔
161
                                punct(":"),
2✔
162
                                deleted(&printer.format_peek(*value))
2✔
UNCOV
163
                            )?;
×
164
                        }
165

166
                        let mut insertions: Vec<_> = insertions.iter().collect();
52✔
167
                        insertions.sort_by(|(a, _), (b, _)| a.cmp(b));
52✔
168
                        for (fld, value) in insertions {
52✔
169
                            writeln!(
2✔
170
                                indent,
2✔
171
                                "{} {}{} {}",
172
                                inserted("+"),
2✔
173
                                field(fld),
2✔
174
                                punct(":"),
2✔
175
                                inserted(&printer.format_peek(*value))
2✔
UNCOV
176
                            )?;
×
177
                        }
178

179
                        write!(f, "{}", punct("}"))
52✔
180
                    }
181
                    Value::Tuple { updates } => {
16✔
182
                        // No changes in tuple
183
                        if updates.is_empty() {
16✔
184
                            return write!(f, "{}", muted("(structurally equal)"));
2✔
185
                        }
14✔
186
                        // For single-element tuples (like Option::Some), try to be concise
187
                        if updates.is_single_replace() {
14✔
188
                            if has_prefix {
10✔
189
                                f.write_str(" ")?;
7✔
190
                            }
3✔
191
                            write!(f, "{updates}")
10✔
192
                        } else {
193
                            f.write_str(if has_prefix { " (\n" } else { "(\n" })?;
4✔
194
                            let mut indent = PadAdapter::new_indented(f);
4✔
195
                            write!(indent, "{updates}")?;
4✔
196
                            f.write_str(")")
4✔
197
                        }
198
                    }
199
                }
200
            }
201
            Diff::Sequence {
202
                from: _,
203
                to: _,
204
                updates,
34✔
205
            } => {
206
                if updates.is_empty() {
34✔
207
                    write!(f, "{}", muted("(structurally equal)"))
5✔
208
                } else {
209
                    writeln!(f, "{}", punct("["))?;
29✔
210
                    let mut indent = PadAdapter::new_indented(f);
29✔
211
                    write!(indent, "{updates}")?;
29✔
212
                    write!(f, "{}", punct("]"))
29✔
213
                }
214
            }
215
        }
216
    }
239✔
217
}
218

219
impl<'mem, 'facet> Updates<'mem, 'facet> {
220
    /// Check if this is a single replace operation (useful for Option::Some)
221
    fn is_single_replace(&self) -> bool {
14✔
222
        self.0.first.is_some() && self.0.values.is_empty() && self.0.last.is_none()
14✔
223
    }
14✔
224

225
    /// Check if there are no changes (everything is unchanged)
226
    fn is_empty(&self) -> bool {
50✔
227
        self.0.first.is_none() && self.0.values.is_empty()
50✔
228
    }
50✔
229
}
230

231
impl<'mem, 'facet> Display for Updates<'mem, 'facet> {
232
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43✔
233
        if let Some(update) = &self.0.first {
43✔
234
            update.fmt(f)?;
26✔
235
        }
17✔
236

237
        for (values, update) in &self.0.values {
43✔
238
            // Collapse kept values into ".. N unchanged items"
239
            let count = values.len();
23✔
240
            if count > 0 {
23✔
241
                let label = if count == 1 { "item" } else { "items" };
23✔
242
                writeln!(f, "{}", muted(&format!(".. {count} unchanged {label}")))?;
23✔
UNCOV
243
            }
×
244
            update.fmt(f)?;
23✔
245
        }
246

247
        if let Some(values) = &self.0.last {
43✔
248
            // Collapse trailing kept values
249
            let count = values.len();
7✔
250
            if count > 0 {
7✔
251
                let label = if count == 1 { "item" } else { "items" };
7✔
252
                writeln!(f, "{}", muted(&format!(".. {count} unchanged {label}")))?;
7✔
UNCOV
253
            }
×
254
        }
36✔
255

256
        Ok(())
43✔
257
    }
43✔
258
}
259

260
impl<'mem, 'facet> Display for ReplaceGroup<'mem, 'facet> {
261
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32✔
262
        let printer = PrettyPrinter::default()
32✔
263
            .with_colors(false)
32✔
264
            .with_minimal_option_names(true);
32✔
265

266
        // If it's a 1-to-1 replacement, check for nested diff or equality
267
        if self.removals.len() == 1 && self.additions.len() == 1 {
32✔
268
            let from = self.removals[0];
14✔
269
            let to = self.additions[0];
14✔
270
            let diff = Diff::new_peek(from, to);
14✔
271

272
            match &diff {
14✔
273
                Diff::Equal { .. } => {
274
                    // Values are equal, show muted
UNCOV
275
                    return writeln!(f, "{}", muted(&printer.format_peek(from)));
×
276
                }
277
                Diff::Replace { .. } => {
278
                    // Simple value change, show inline: old → new
279
                    return writeln!(f, "{diff}");
14✔
280
                }
281
                _ => {
282
                    // Has nested structure, show the diff
UNCOV
283
                    return writeln!(f, "{diff}");
×
284
                }
285
            }
286
        }
18✔
287

288
        // Otherwise show as - / + lines with consistent indentation
289
        for remove in &self.removals {
18✔
290
            writeln!(
14✔
291
                f,
14✔
292
                "{}",
293
                deleted(&format!("- {}", printer.format_peek(*remove)))
14✔
UNCOV
294
            )?;
×
295
        }
296

297
        for add in &self.additions {
18✔
298
            writeln!(
17✔
299
                f,
17✔
300
                "{}",
301
                inserted(&format!("+ {}", printer.format_peek(*add)))
17✔
UNCOV
302
            )?;
×
303
        }
304

305
        Ok(())
18✔
306
    }
32✔
307
}
308

309
/// Write a sequence of diffs, collapsing Equal diffs into ".. N unchanged items"
310
fn write_diff_sequence(
17✔
311
    f: &mut std::fmt::Formatter<'_>,
17✔
312
    diffs: &[Diff<'_, '_>],
17✔
313
) -> std::fmt::Result {
17✔
314
    let mut i = 0;
17✔
315
    while i < diffs.len() {
58✔
316
        // Count consecutive Equal diffs
317
        let mut equal_count = 0;
41✔
318
        while i + equal_count < diffs.len() {
53✔
319
            if matches!(diffs[i + equal_count], Diff::Equal { .. }) {
53✔
320
                equal_count += 1;
12✔
321
            } else {
12✔
322
                break;
41✔
323
            }
324
        }
325

326
        if equal_count > 0 {
41✔
327
            // Collapse Equal diffs
328
            let label = if equal_count == 1 { "item" } else { "items" };
11✔
329
            writeln!(
11✔
330
                f,
11✔
331
                "{}",
332
                muted(&format!(".. {equal_count} unchanged {label}"))
11✔
UNCOV
333
            )?;
×
334
            i += equal_count;
11✔
335
        } else {
336
            // Show the non-Equal diff
337
            writeln!(f, "{}", diffs[i])?;
30✔
338
            i += 1;
30✔
339
        }
340
    }
341
    Ok(())
17✔
342
}
17✔
343

344
impl<'mem, 'facet> Display for UpdatesGroup<'mem, 'facet> {
345
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49✔
346
        if let Some(update) = &self.0.first {
49✔
347
            update.fmt(f)?;
32✔
348
        }
17✔
349

350
        for (values, update) in &self.0.values {
49✔
UNCOV
351
            write_diff_sequence(f, values)?;
×
UNCOV
352
            update.fmt(f)?;
×
353
        }
354

355
        if let Some(values) = &self.0.last {
49✔
356
            write_diff_sequence(f, values)?;
17✔
357
        }
32✔
358

359
        Ok(())
49✔
360
    }
49✔
361
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc