1use std::fmt::{self, Display, Write};
2
3use crate::{Colour, Style};
4
5impl Style {
6 /// Write any bytes that go *before* a piece of text to the given writer.
7 pub fn write_prefix(&self, f: &mut fmt::Formatter) -> Result<bool, fmt::Error> {
8 let mut written_anything = false;
9 macro_rules! write_anything {
10 () => {
11 if written_anything {
12 f.write_char(';')?;
13 } else {
14 // Write the codes’ prefix, then write numbers, separated by
15 // semicolons, for each text style we want to apply.
16 f.write_str("\x1B[")?;
17 written_anything = true;
18 }
19 };
20 }
21 macro_rules! write_char {
22 ($cond:ident, $c:expr) => {
23 if self.$cond {
24 write_anything!();
25 f.write_char($c)?;
26 }
27 };
28 }
29 macro_rules! write_chars {
30 ($cond:ident => $c:expr) => { write_char!($cond, $c); };
31 ($cond:ident => $c:expr, $($t:tt)+) => {
32 write_char!($cond, $c);
33 write_chars!($($t)+);
34 };
35 }
36
37 write_chars!(
38 is_bold => '1',
39 is_dimmed => '2',
40 is_italic => '3',
41 is_underline => '4',
42 is_blink => '5',
43 is_reverse => '7',
44 is_hidden => '8',
45 is_strikethrough => '9'
46 );
47
48 // The foreground and background colours, if specified, need to be
49 // handled specially because the number codes are more complicated.
50 // (see `write_background_code` and `write_foreground_code`)
51 if let Some(bg) = self.background {
52 write_anything!();
53 bg.write_background_code(f)?;
54 }
55
56 if let Some(fg) = self.foreground {
57 write_anything!();
58 fg.write_foreground_code(f)?;
59 }
60
61 if written_anything {
62 // All the codes end with an `m`, because reasons.
63 f.write_char('m')?;
64 }
65
66 Ok(written_anything)
67 }
68
69 /// Write any bytes that go *after* a piece of text to the given writer.
70 #[inline]
71 pub fn write_reset(f: &mut fmt::Formatter) -> fmt::Result {
72 f.write_str(RESET)
73 }
74}
75
76impl Colour {
77 /// Write any bytes that go *before* a piece of text to the given writer.
78 #[inline]
79 pub fn write_prefix(self, f: &mut fmt::Formatter) -> Result<bool, fmt::Error> {
80 self.normal().write_prefix(f)
81 }
82}
83
84/// The code to send to reset all styles and return to `Style::default()`.
85pub static RESET: &str = "\x1B[0m";
86
87macro_rules! write_color {
88 ($_self:ident, $f:ident =>
89 $black:expr, $red:expr, $green:expr, $yellow:expr, $blue:expr,
90 $purple:expr, $cyan:expr, $white:expr, $fixed:expr, $rgb:expr) => {{
91 use Colour::*;
92 match $_self {
93 Black => $f.write_str($black),
94 Red => $f.write_str($red),
95 Green => $f.write_str($green),
96 Yellow => $f.write_str($yellow),
97 Blue => $f.write_str($blue),
98 Purple => $f.write_str($purple),
99 Cyan => $f.write_str($cyan),
100 White => $f.write_str($white),
101 Fixed(num) => {
102 $f.write_str($fixed)?;
103 num.fmt($f)
104 }
105 RGB(r, g, b) => {
106 $f.write_str($rgb)?;
107 r.fmt($f)?;
108 $f.write_char(';')?;
109 g.fmt($f)?;
110 $f.write_char(';')?;
111 b.fmt($f)
112 }
113 }
114 }};
115}
116
117impl Colour {
118 #[inline]
119 fn write_foreground_code(self, f: &mut fmt::Formatter) -> fmt::Result {
120 write_color!(self, f => "30", "31", "32", "33", "34", "35", "36", "37", "38;5;", "38;2;")
121 }
122
123 #[inline]
124 fn write_background_code(self, f: &mut fmt::Formatter) -> fmt::Result {
125 write_color!(self, f => "40", "41", "42", "43", "44", "45", "46", "47", "48;5;", "48;2;")
126 }
127}
128
129#[cfg(test)]
130mod test {
131 use crate::{Colour::*, Style};
132
133 macro_rules! test {
134 ($name: ident: $style: expr; $input: expr => $result: expr) => {
135 #[test]
136 fn $name() {
137 assert_eq!($style.paint($input).to_string(), $result.to_string());
138 }
139 };
140 }
141
142 test!(plain: Style::default(); "text/plain" => "text/plain");
143 test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m");
144 test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m");
145 test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
146 test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
147 test!(blue_underline: Blue.underline(); "hi" => "\x1B[4;34mhi\x1B[0m");
148 test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
149 test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
150 test!(purple_on_white: Purple.on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
151 test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
152 test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
153 test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
154 test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[1;47;36mhi\x1B[0m");
155 test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[4;47;36mhi\x1B[0m");
156 test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
157 test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
158 test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
159 test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
160 test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
161 test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
162 test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
163 test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
164 test!(rgb_on_rgb: RGB(70,130,180).on(RGB(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
165 test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
166 test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
167 test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");
168 test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m");
169 test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m");
170 test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m");
171 test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m");
172 test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m");
173 test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m");
174
175 macro_rules! test_fn {
176 ($name:ident: $style:expr; $result:expr) => {
177 #[test]
178 fn $name() {
179 let string = String::from("hi");
180 let string: &str = &string;
181 assert_eq!(
182 $style.paint_fn(|f| f.write_str(string)).to_string(),
183 $result.to_string()
184 );
185 }
186 };
187 }
188
189 test_fn!(plain_fn: Style::default(); "hi");
190 test_fn!(red_fn: Red; "\x1B[31mhi\x1B[0m");
191 test_fn!(black_fn: Black.normal(); "\x1B[30mhi\x1B[0m");
192 test_fn!(yellow_bold_fn: Yellow.bold(); "\x1B[1;33mhi\x1B[0m");
193 test_fn!(yellow_bold_2_fn: Yellow.normal().bold(); "\x1B[1;33mhi\x1B[0m");
194 test_fn!(blue_underline_fn: Blue.underline(); "\x1B[4;34mhi\x1B[0m");
195 test_fn!(green_bold_ul_fn: Green.bold().underline(); "\x1B[1;4;32mhi\x1B[0m");
196 test_fn!(green_bold_ul_2_fn: Green.underline().bold(); "\x1B[1;4;32mhi\x1B[0m");
197 test_fn!(purple_on_white_fn: Purple.on(White); "\x1B[47;35mhi\x1B[0m");
198 test_fn!(purple_on_white_2_fn: Purple.normal().on(White); "\x1B[47;35mhi\x1B[0m");
199 test_fn!(yellow_on_blue_fn: Style::new().on(Blue).fg(Yellow); "\x1B[44;33mhi\x1B[0m");
200 test_fn!(yellow_on_blue_2_fn: Cyan.on(Blue).fg(Yellow); "\x1B[44;33mhi\x1B[0m");
201 test_fn!(cyan_bold_on_white_fn: Cyan.bold().on(White); "\x1B[1;47;36mhi\x1B[0m");
202 test_fn!(cyan_ul_on_white_fn: Cyan.underline().on(White); "\x1B[4;47;36mhi\x1B[0m");
203 test_fn!(cyan_bold_ul_on_white_fn: Cyan.bold().underline().on(White); "\x1B[1;4;47;36mhi\x1B[0m");
204 test_fn!(cyan_ul_bold_on_white_fn: Cyan.underline().bold().on(White); "\x1B[1;4;47;36mhi\x1B[0m");
205 test_fn!(fixed_fn: Fixed(100); "\x1B[38;5;100mhi\x1B[0m");
206 test_fn!(fixed_on_purple_fn: Fixed(100).on(Purple); "\x1B[45;38;5;100mhi\x1B[0m");
207 test_fn!(fixed_on_fixed_fn: Fixed(100).on(Fixed(200)); "\x1B[48;5;200;38;5;100mhi\x1B[0m");
208 test_fn!(rgb_fn: RGB(70,130,180); "\x1B[38;2;70;130;180mhi\x1B[0m");
209 test_fn!(rgb_on_blue_fn: RGB(70,130,180).on(Blue); "\x1B[44;38;2;70;130;180mhi\x1B[0m");
210 test_fn!(blue_on_rgb_fn: Blue.on(RGB(70,130,180)); "\x1B[48;2;70;130;180;34mhi\x1B[0m");
211 test_fn!(rgb_on_rgb_fn: RGB(70,130,180).on(RGB(5,10,15)); "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
212 test_fn!(bold_fn: Style::new().bold(); "\x1B[1mhi\x1B[0m");
213 test_fn!(underline_fn: Style::new().underline(); "\x1B[4mhi\x1B[0m");
214 test_fn!(bunderline_fn: Style::new().bold().underline(); "\x1B[1;4mhi\x1B[0m");
215 test_fn!(dimmed_fn: Style::new().dimmed(); "\x1B[2mhi\x1B[0m");
216 test_fn!(italic_fn: Style::new().italic(); "\x1B[3mhi\x1B[0m");
217 test_fn!(blink_fn: Style::new().blink(); "\x1B[5mhi\x1B[0m");
218 test_fn!(reverse_fn: Style::new().reverse(); "\x1B[7mhi\x1B[0m");
219 test_fn!(hidden_fn: Style::new().hidden(); "\x1B[8mhi\x1B[0m");
220 test_fn!(stricken_fn: Style::new().strikethrough(); "\x1B[9mhi\x1B[0m");
221
222 #[test]
223 fn test_move() {
224 let string = String::from("hi");
225 assert_eq!(
226 Style::default()
227 .paint_fn(|f| f.write_str(&string))
228 .to_string(),
229 "hi"
230 );
231 }
232
233 #[test]
234 fn test_ref() {
235 let string = &String::from("hi");
236 assert_eq!(
237 Style::default()
238 .paint_fn(|f| f.write_str(string))
239 .to_string(),
240 "hi"
241 );
242 }
243
244 #[test]
245 fn test_debug() {
246 let a = vec![1, 2, 3];
247 assert_eq!(
248 Style::default()
249 .paint_fn(|f| std::fmt::Debug::fmt(&a, f))
250 .to_string(),
251 "[1, 2, 3]"
252 );
253 assert_eq!(
254 Style::default()
255 .bold()
256 .paint_fn(|f| std::fmt::Debug::fmt(&a, f))
257 .to_string(),
258 "\x1B[1m[1, 2, 3]\x1B[0m"
259 );
260 }
261
262 #[test]
263 fn test_write() {
264 assert_eq!(
265 Style::default()
266 .paint_fn(|f| write!(f, "{:.5}", 1.0))
267 .to_string(),
268 "1.00000"
269 );
270 assert_eq!(
271 Style::default()
272 .bold()
273 .paint_fn(|f| write!(f, "{:.5}", 1.0))
274 .to_string(),
275 "\x1B[1m1.00000\x1B[0m"
276 );
277 }
278
279 /// Can not write the same `impl Display` two or more times
280 /// else return error
281 #[test]
282 fn test_error() {
283 use std::fmt::Write;
284 let a = Style::default().paint("foo");
285 let _ = a.to_string();
286 let mut b = String::new();
287
288 assert!(write!(b, "{}", a).is_err());
289 }
290}
291