Coverage Report

Created: 2025-07-11 07:02

/rust/registry/src/index.crates.io-6f17d22bba15001f/nu-ansi-term-0.49.0/src/difference.rs
Line
Count
Source (jump to first uncovered line)
1
use super::Style;
2
3
/// When printing out one colored string followed by another, use one of
4
/// these rules to figure out which *extra* control codes need to be sent.
5
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
6
pub enum Difference {
7
    /// Print out the control codes specified by this style to end up looking
8
    /// like the second string's styles.
9
    ExtraStyles(Style),
10
11
    /// Converting between these two is impossible, so just send a reset
12
    /// command and then the second string's styles.
13
    Reset,
14
15
    /// The before style is exactly the same as the after style, so no further
16
    /// control codes need to be printed.
17
    Empty,
18
}
19
20
impl Difference {
21
    /// Compute the 'style difference' required to turn an existing style into
22
    /// the given, second style.
23
    ///
24
    /// For example, to turn green text into green bold text, it's redundant
25
    /// to write a reset command then a second green+bold command, instead of
26
    /// just writing one bold command. This method should see that both styles
27
    /// use the foreground color green, and reduce it to a single command.
28
    ///
29
    /// This method returns an enum value because it's not actually always
30
    /// possible to turn one style into another: for example, text could be
31
    /// made bold and underlined, but you can't remove the bold property
32
    /// without also removing the underline property. So when this has to
33
    /// happen, this function returns None, meaning that the entire set of
34
    /// styles should be reset and begun again.
35
0
    pub fn between(first: &Style, next: &Style) -> Difference {
36
        use self::Difference::*;
37
38
        // XXX(Havvy): This algorithm is kind of hard to replicate without
39
        // having the Plain/Foreground enum variants, so I'm just leaving
40
        // it commented out for now, and defaulting to Reset.
41
42
0
        if first == next {
43
0
            return Empty;
44
0
        }
45
0
46
0
        // Cannot un-bold, so must Reset.
47
0
        if first.is_bold && !next.is_bold {
48
0
            return Reset;
49
0
        }
50
0
51
0
        if first.is_dimmed && !next.is_dimmed {
52
0
            return Reset;
53
0
        }
54
0
55
0
        if first.is_italic && !next.is_italic {
56
0
            return Reset;
57
0
        }
58
0
59
0
        // Cannot un-underline, so must Reset.
60
0
        if first.is_underline && !next.is_underline {
61
0
            return Reset;
62
0
        }
63
0
64
0
        if first.is_blink && !next.is_blink {
65
0
            return Reset;
66
0
        }
67
0
68
0
        if first.is_reverse && !next.is_reverse {
69
0
            return Reset;
70
0
        }
71
0
72
0
        if first.is_hidden && !next.is_hidden {
73
0
            return Reset;
74
0
        }
75
0
76
0
        if first.is_strikethrough && !next.is_strikethrough {
77
0
            return Reset;
78
0
        }
79
0
80
0
        // Cannot go from foreground to no foreground, so must Reset.
81
0
        if first.foreground.is_some() && next.foreground.is_none() {
82
0
            return Reset;
83
0
        }
84
0
85
0
        // Cannot go from background to no background, so must Reset.
86
0
        if first.background.is_some() && next.background.is_none() {
87
0
            return Reset;
88
0
        }
89
0
90
0
        let mut extra_styles = Style::default();
91
0
92
0
        if first.is_bold != next.is_bold {
93
0
            extra_styles.is_bold = true;
94
0
        }
95
96
0
        if first.is_dimmed != next.is_dimmed {
97
0
            extra_styles.is_dimmed = true;
98
0
        }
99
100
0
        if first.is_italic != next.is_italic {
101
0
            extra_styles.is_italic = true;
102
0
        }
103
104
0
        if first.is_underline != next.is_underline {
105
0
            extra_styles.is_underline = true;
106
0
        }
107
108
0
        if first.is_blink != next.is_blink {
109
0
            extra_styles.is_blink = true;
110
0
        }
111
112
0
        if first.is_reverse != next.is_reverse {
113
0
            extra_styles.is_reverse = true;
114
0
        }
115
116
0
        if first.is_hidden != next.is_hidden {
117
0
            extra_styles.is_hidden = true;
118
0
        }
119
120
0
        if first.is_strikethrough != next.is_strikethrough {
121
0
            extra_styles.is_strikethrough = true;
122
0
        }
123
124
0
        if first.foreground != next.foreground {
125
0
            extra_styles.foreground = next.foreground;
126
0
        }
127
128
0
        if first.background != next.background {
129
0
            extra_styles.background = next.background;
130
0
        }
131
132
0
        ExtraStyles(extra_styles)
133
0
    }
134
}
135
136
#[cfg(test)]
137
mod test {
138
    use super::Difference::*;
139
    use super::*;
140
    use crate::style::Color::*;
141
    use crate::style::Style;
142
143
    fn style() -> Style {
144
        Style::new()
145
    }
146
147
    macro_rules! test {
148
        ($name: ident: $first: expr; $next: expr => $result: expr) => {
149
            #[test]
150
            fn $name() {
151
                assert_eq!($result, Difference::between(&$first, &$next));
152
            }
153
        };
154
    }
155
156
    test!(nothing:    Green.normal(); Green.normal()  => Empty);
157
    test!(uppercase:  Green.normal(); Green.bold()    => ExtraStyles(style().bold()));
158
    test!(lowercase:  Green.bold();   Green.normal()  => Reset);
159
    test!(nothing2:   Green.bold();   Green.bold()    => Empty);
160
161
    test!(color_change: Red.normal(); Blue.normal() => ExtraStyles(Blue.normal()));
162
163
    test!(addition_of_blink:          style(); style().blink()          => ExtraStyles(style().blink()));
164
    test!(addition_of_dimmed:         style(); style().dimmed()         => ExtraStyles(style().dimmed()));
165
    test!(addition_of_hidden:         style(); style().hidden()         => ExtraStyles(style().hidden()));
166
    test!(addition_of_reverse:        style(); style().reverse()        => ExtraStyles(style().reverse()));
167
    test!(addition_of_strikethrough:  style(); style().strikethrough()  => ExtraStyles(style().strikethrough()));
168
169
    test!(removal_of_strikethrough:   style().strikethrough(); style()  => Reset);
170
    test!(removal_of_reverse:         style().reverse();       style()  => Reset);
171
    test!(removal_of_hidden:          style().hidden();        style()  => Reset);
172
    test!(removal_of_dimmed:          style().dimmed();        style()  => Reset);
173
    test!(removal_of_blink:           style().blink();         style()  => Reset);
174
}