1use super::Style;
23/// 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(PartialEq, Clone, Copy, Debug)]
6pub enum Difference {
7/// Print out the control codes specified by this style to end up looking
8 /// like the second string's styles.
9ExtraStyles(Style),
1011/// Converting between these two is impossible, so just send a reset
12 /// command and then the second string's styles.
13Reset,
1415/// The before style is exactly the same as the after style, so no further
16 /// control codes need to be printed.
17Empty,
18}
1920impl 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.
35pub fn between(first: &Style, next: &Style) -> Difference {
36use self::Difference::*;
3738// 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.
4142if first == next {
43return Empty;
44 }
4546// Cannot un-bold, so must Reset.
47if first.is_bold && !next.is_bold {
48return Reset;
49 }
5051if first.is_dimmed && !next.is_dimmed {
52return Reset;
53 }
5455if first.is_italic && !next.is_italic {
56return Reset;
57 }
5859// Cannot un-underline, so must Reset.
60if first.is_underline && !next.is_underline {
61return Reset;
62 }
6364if first.is_blink && !next.is_blink {
65return Reset;
66 }
6768if first.is_reverse && !next.is_reverse {
69return Reset;
70 }
7172if first.is_hidden && !next.is_hidden {
73return Reset;
74 }
7576if first.is_strikethrough && !next.is_strikethrough {
77return Reset;
78 }
7980// Cannot go from foreground to no foreground, so must Reset.
81if first.foreground.is_some() && next.foreground.is_none() {
82return Reset;
83 }
8485// Cannot go from background to no background, so must Reset.
86if first.background.is_some() && next.background.is_none() {
87return Reset;
88 }
8990let mut extra_styles = Style::default();
9192if first.is_bold != next.is_bold {
93 extra_styles.is_bold = true;
94 }
9596if first.is_dimmed != next.is_dimmed {
97 extra_styles.is_dimmed = true;
98 }
99100if first.is_italic != next.is_italic {
101 extra_styles.is_italic = true;
102 }
103104if first.is_underline != next.is_underline {
105 extra_styles.is_underline = true;
106 }
107108if first.is_blink != next.is_blink {
109 extra_styles.is_blink = true;
110 }
111112if first.is_reverse != next.is_reverse {
113 extra_styles.is_reverse = true;
114 }
115116if first.is_hidden != next.is_hidden {
117 extra_styles.is_hidden = true;
118 }
119120if first.is_strikethrough != next.is_strikethrough {
121 extra_styles.is_strikethrough = true;
122 }
123124if first.foreground != next.foreground {
125 extra_styles.foreground = next.foreground;
126 }
127128if first.background != next.background {
129 extra_styles.background = next.background;
130 }
131132 ExtraStyles(extra_styles)
133 }
134}
135136#[cfg(test)]
137mod test {
138use super::Difference::*;
139use super::*;
140use crate::style::Color::*;
141use crate::style::Style;
142143fn style() -> Style {
144 Style::new()
145 }
146147macro_rules! test {
148 ($name: ident: $first: expr; $next: expr => $result: expr) => {
149#[test]
150fn $name() {
151assert_eq!($result, Difference::between(&$first, &$next));
152 }
153 };
154 }
155156test!(nothing: Green.normal(); Green.normal() => Empty);
157test!(uppercase: Green.normal(); Green.bold() => ExtraStyles(style().bold()));
158test!(lowercase: Green.bold(); Green.normal() => Reset);
159test!(nothing2: Green.bold(); Green.bold() => Empty);
160161test!(color_change: Red.normal(); Blue.normal() => ExtraStyles(Blue.normal()));
162163test!(addition_of_blink: style(); style().blink() => ExtraStyles(style().blink()));
164test!(addition_of_dimmed: style(); style().dimmed() => ExtraStyles(style().dimmed()));
165test!(addition_of_hidden: style(); style().hidden() => ExtraStyles(style().hidden()));
166test!(addition_of_reverse: style(); style().reverse() => ExtraStyles(style().reverse()));
167test!(addition_of_strikethrough: style(); style().strikethrough() => ExtraStyles(style().strikethrough()));
168169test!(removal_of_strikethrough: style().strikethrough(); style() => Reset);
170test!(removal_of_reverse: style().reverse(); style() => Reset);
171test!(removal_of_hidden: style().hidden(); style() => Reset);
172test!(removal_of_dimmed: style().dimmed(); style() => Reset);
173test!(removal_of_blink: style().blink(); style() => Reset);
174}