1/// A style is a collection of properties that can format a string
2/// using ANSI escape codes.
3///
4/// # Examples
5///
6/// ```
7/// use yansi_term::{Style, Colour};
8///
9/// let style = Style::new().bold().on(Colour::Black);
10/// println!("{}", style.paint("Bold on black"));
11/// ```
12#[derive(PartialEq, Clone, Copy, Default, Debug)]
13#[cfg_attr(
14 feature = "derive_serde_style",
15 derive(serde::Deserialize, serde::Serialize)
16)]
17pub struct Style {
18 /// The style's foreground colour, if it has one.
19 pub foreground: Option<Colour>,
20
21 /// The style's background colour, if it has one.
22 pub background: Option<Colour>,
23
24 /// Whether this style is bold.
25 pub is_bold: bool,
26
27 /// Whether this style is dimmed.
28 pub is_dimmed: bool,
29
30 /// Whether this style is italic.
31 pub is_italic: bool,
32
33 /// Whether this style is underlined.
34 pub is_underline: bool,
35
36 /// Whether this style is blinking.
37 pub is_blink: bool,
38
39 /// Whether this style has reverse colours.
40 pub is_reverse: bool,
41
42 /// Whether this style is hidden.
43 pub is_hidden: bool,
44
45 /// Whether this style is struckthrough.
46 pub is_strikethrough: bool,
47}
48
49impl Style {
50 /// Creates a new Style with no properties set.
51 ///
52 /// # Examples
53 ///
54 /// ```
55 /// use yansi_term::Style;
56 ///
57 /// let style = Style::new();
58 /// println!("{}", style.paint("hi"));
59 /// ```
60 pub fn new() -> Style {
61 Style::default()
62 }
63
64 /// Returns a `Style` with the bold property set.
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use yansi_term::Style;
70 ///
71 /// let style = Style::new().bold();
72 /// println!("{}", style.paint("hey"));
73 /// ```
74 pub fn bold(mut self) -> Self {
75 self.is_bold = true;
76 self
77 }
78
79 /// Returns a `Style` with the dimmed property set.
80 ///
81 /// # Examples
82 ///
83 /// ```
84 /// use yansi_term::Style;
85 ///
86 /// let style = Style::new().dimmed();
87 /// println!("{}", style.paint("sup"));
88 /// ```
89 pub fn dimmed(mut self) -> Self {
90 self.is_dimmed = true;
91 self
92 }
93
94 /// Returns a `Style` with the italic property set.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// use yansi_term::Style;
100 ///
101 /// let style = Style::new().italic();
102 /// println!("{}", style.paint("greetings"));
103 /// ```
104 pub fn italic(mut self) -> Self {
105 self.is_italic = true;
106 self
107 }
108
109 /// Returns a `Style` with the underline property set.
110 ///
111 /// # Examples
112 ///
113 /// ```
114 /// use yansi_term::Style;
115 ///
116 /// let style = Style::new().underline();
117 /// println!("{}", style.paint("salutations"));
118 /// ```
119 pub fn underline(mut self) -> Self {
120 self.is_underline = true;
121 self
122 }
123
124 /// Returns a `Style` with the blink property set.
125 /// # Examples
126 ///
127 /// ```
128 /// use yansi_term::Style;
129 ///
130 /// let style = Style::new().blink();
131 /// println!("{}", style.paint("wazzup"));
132 /// ```
133 pub fn blink(mut self) -> Self {
134 self.is_blink = true;
135 self
136 }
137
138 /// Returns a `Style` with the reverse property set.
139 ///
140 /// # Examples
141 ///
142 /// ```
143 /// use yansi_term::Style;
144 ///
145 /// let style = Style::new().reverse();
146 /// println!("{}", style.paint("aloha"));
147 /// ```
148 pub fn reverse(mut self) -> Self {
149 self.is_reverse = true;
150 self
151 }
152
153 /// Returns a `Style` with the hidden property set.
154 ///
155 /// # Examples
156 ///
157 /// ```
158 /// use yansi_term::Style;
159 ///
160 /// let style = Style::new().hidden();
161 /// println!("{}", style.paint("ahoy"));
162 /// ```
163 pub fn hidden(mut self) -> Self {
164 self.is_hidden = true;
165 self
166 }
167
168 /// Returns a `Style` with the strikethrough property set.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use yansi_term::Style;
174 ///
175 /// let style = Style::new().strikethrough();
176 /// println!("{}", style.paint("yo"));
177 /// ```
178 pub fn strikethrough(mut self) -> Self {
179 self.is_strikethrough = true;
180 self
181 }
182
183 /// Returns a `Style` with the foreground colour property set.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use yansi_term::{Style, Colour};
189 ///
190 /// let style = Style::new().fg(Colour::Yellow);
191 /// println!("{}", style.paint("hi"));
192 /// ```
193 pub fn fg(mut self, foreground: Colour) -> Self {
194 self.foreground = Some(foreground);
195 self
196 }
197
198 /// Returns a `Style` with the background colour property set.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use yansi_term::{Style, Colour};
204 ///
205 /// let style = Style::new().on(Colour::Blue);
206 /// println!("{}", style.paint("eyyyy"));
207 /// ```
208 pub fn on(mut self, background: Colour) -> Self {
209 self.background = Some(background);
210 self
211 }
212
213 /// Return true if this `Style` has no actual styles, and can be written
214 /// without any control characters.
215 ///
216 /// # Examples
217 ///
218 /// ```
219 /// use yansi_term::Style;
220 ///
221 /// assert_eq!(true, Style::default().is_plain());
222 /// assert_eq!(false, Style::default().bold().is_plain());
223 /// ```
224 pub fn is_plain(&self) -> bool {
225 *self == Style::default()
226 }
227}
228
229/// A colour is one specific type of ANSI escape code, and can refer
230/// to either the foreground or background colour.
231///
232/// These use the standard numeric sequences.
233/// See <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
234#[derive(PartialEq, Clone, Copy, Debug)]
235#[cfg_attr(
236 feature = "derive_serde_style",
237 derive(serde::Deserialize, serde::Serialize)
238)]
239pub enum Colour {
240 /// Colour #0 (foreground code `30`, background code `40`).
241 ///
242 /// This is not necessarily the background colour, and using it as one may
243 /// render the text hard to read on terminals with dark backgrounds.
244 Black,
245
246 /// Colour #1 (foreground code `31`, background code `41`).
247 Red,
248
249 /// Colour #2 (foreground code `32`, background code `42`).
250 Green,
251
252 /// Colour #3 (foreground code `33`, background code `43`).
253 Yellow,
254
255 /// Colour #4 (foreground code `34`, background code `44`).
256 Blue,
257
258 /// Colour #5 (foreground code `35`, background code `45`).
259 Purple,
260
261 /// Colour #6 (foreground code `36`, background code `46`).
262 Cyan,
263
264 /// Colour #7 (foreground code `37`, background code `47`).
265 ///
266 /// As above, this is not necessarily the foreground colour, and may be
267 /// hard to read on terminals with light backgrounds.
268 White,
269
270 /// A colour number from 0 to 255, for use in 256-colour terminal
271 /// environments.
272 ///
273 /// - Colours 0 to 7 are the `Black` to `White` variants respectively.
274 /// These colours can usually be changed in the terminal emulator.
275 /// - Colours 8 to 15 are brighter versions of the eight colours above.
276 /// These can also usually be changed in the terminal emulator, or it
277 /// could be configured to use the original colours and show the text in
278 /// bold instead. It varies depending on the program.
279 /// - Colours 16 to 231 contain several palettes of bright colours,
280 /// arranged in six squares measuring six by six each.
281 /// - Colours 232 to 255 are shades of grey from black to white.
282 ///
283 /// It might make more sense to look at a [colour chart][cc].
284 ///
285 /// [cc]: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
286 Fixed(u8),
287
288 /// A 24-bit RGB color, as specified by ISO-8613-3.
289 RGB(u8, u8, u8),
290}
291
292impl Colour {
293 /// Returns a `Style` with the foreground colour set to this colour.
294 ///
295 /// # Examples
296 ///
297 /// ```
298 /// use yansi_term::Colour;
299 ///
300 /// let style = Colour::Red.normal();
301 /// println!("{}", style.paint("hi"));
302 /// ```
303 pub fn normal(self) -> Style {
304 Style {
305 foreground: Some(self),
306 ..Style::default()
307 }
308 }
309
310 /// Returns a `Style` with the foreground colour set to this colour and the
311 /// bold property set.
312 ///
313 /// # Examples
314 ///
315 /// ```
316 /// use yansi_term::Colour;
317 ///
318 /// let style = Colour::Green.bold();
319 /// println!("{}", style.paint("hey"));
320 /// ```
321 pub fn bold(self) -> Style {
322 Style {
323 foreground: Some(self),
324 is_bold: true,
325 ..Style::default()
326 }
327 }
328
329 /// Returns a `Style` with the foreground colour set to this colour and the
330 /// dimmed property set.
331 ///
332 /// # Examples
333 ///
334 /// ```
335 /// use yansi_term::Colour;
336 ///
337 /// let style = Colour::Yellow.dimmed();
338 /// println!("{}", style.paint("sup"));
339 /// ```
340 pub fn dimmed(self) -> Style {
341 Style {
342 foreground: Some(self),
343 is_dimmed: true,
344 ..Style::default()
345 }
346 }
347
348 /// Returns a `Style` with the foreground colour set to this colour and the
349 /// italic property set.
350 ///
351 /// # Examples
352 ///
353 /// ```
354 /// use yansi_term::Colour;
355 ///
356 /// let style = Colour::Blue.italic();
357 /// println!("{}", style.paint("greetings"));
358 /// ```
359 pub fn italic(self) -> Style {
360 Style {
361 foreground: Some(self),
362 is_italic: true,
363 ..Style::default()
364 }
365 }
366
367 /// Returns a `Style` with the foreground colour set to this colour and the
368 /// underline property set.
369 ///
370 /// # Examples
371 ///
372 /// ```
373 /// use yansi_term::Colour;
374 ///
375 /// let style = Colour::Purple.underline();
376 /// println!("{}", style.paint("salutations"));
377 /// ```
378 pub fn underline(self) -> Style {
379 Style {
380 foreground: Some(self),
381 is_underline: true,
382 ..Style::default()
383 }
384 }
385
386 /// Returns a `Style` with the foreground colour set to this colour and the
387 /// blink property set.
388 ///
389 /// # Examples
390 ///
391 /// ```
392 /// use yansi_term::Colour;
393 ///
394 /// let style = Colour::Cyan.blink();
395 /// println!("{}", style.paint("wazzup"));
396 /// ```
397 pub fn blink(self) -> Style {
398 Style {
399 foreground: Some(self),
400 is_blink: true,
401 ..Style::default()
402 }
403 }
404
405 /// Returns a `Style` with the foreground colour set to this colour and the
406 /// reverse property set.
407 ///
408 /// # Examples
409 ///
410 /// ```
411 /// use yansi_term::Colour;
412 ///
413 /// let style = Colour::Black.reverse();
414 /// println!("{}", style.paint("aloha"));
415 /// ```
416 pub fn reverse(self) -> Style {
417 Style {
418 foreground: Some(self),
419 is_reverse: true,
420 ..Style::default()
421 }
422 }
423
424 /// Returns a `Style` with the foreground colour set to this colour and the
425 /// hidden property set.
426 ///
427 /// # Examples
428 ///
429 /// ```
430 /// use yansi_term::Colour;
431 ///
432 /// let style = Colour::White.hidden();
433 /// println!("{}", style.paint("ahoy"));
434 /// ```
435 pub fn hidden(self) -> Style {
436 Style {
437 foreground: Some(self),
438 is_hidden: true,
439 ..Style::default()
440 }
441 }
442
443 /// Returns a `Style` with the foreground colour set to this colour and the
444 /// strikethrough property set.
445 ///
446 /// # Examples
447 ///
448 /// ```
449 /// use yansi_term::Colour;
450 ///
451 /// let style = Colour::Fixed(244).strikethrough();
452 /// println!("{}", style.paint("yo"));
453 /// ```
454 pub fn strikethrough(self) -> Style {
455 Style {
456 foreground: Some(self),
457 is_strikethrough: true,
458 ..Style::default()
459 }
460 }
461
462 /// Returns a `Style` with the foreground colour set to this colour and the
463 /// background colour property set to the given colour.
464 ///
465 /// # Examples
466 ///
467 /// ```
468 /// use yansi_term::Colour;
469 ///
470 /// let style = Colour::RGB(31, 31, 31).on(Colour::White);
471 /// println!("{}", style.paint("eyyyy"));
472 /// ```
473 pub fn on(self, background: Colour) -> Style {
474 Style {
475 foreground: Some(self),
476 background: Some(background),
477 ..Style::default()
478 }
479 }
480}
481
482impl From<Colour> for Style {
483 /// You can turn a `Colour` into a `Style` with the foreground colour set
484 /// with the `From` trait.
485 ///
486 /// ```
487 /// use yansi_term::{Style, Colour};
488 /// let green_foreground = Style::default().fg(Colour::Green);
489 /// assert_eq!(green_foreground, Colour::Green.normal());
490 /// assert_eq!(green_foreground, Colour::Green.into());
491 /// assert_eq!(green_foreground, Style::from(Colour::Green));
492 /// ```
493 fn from(colour: Colour) -> Style {
494 colour.normal()
495 }
496}
497
498#[cfg(test)]
499#[cfg(feature = "derive_serde_style")]
500mod serde_json_tests {
501 use super::{Colour, Style};
502
503 #[test]
504 fn colour_serialization() {
505 let colours = &[
506 Colour::Red,
507 Colour::Blue,
508 Colour::RGB(123, 123, 123),
509 Colour::Fixed(255),
510 ];
511
512 assert_eq!(
513 serde_json::to_string(&colours).unwrap(),
514 String::from("[\"Red\",\"Blue\",{\"RGB\":[123,123,123]},{\"Fixed\":255}]")
515 );
516 }
517
518 #[test]
519 fn colour_deserialization() {
520 let colours = &[
521 Colour::Red,
522 Colour::Blue,
523 Colour::RGB(123, 123, 123),
524 Colour::Fixed(255),
525 ];
526
527 for colour in colours {
528 let serialized = serde_json::to_string(&colour).unwrap();
529 let deserialized: Colour = serde_json::from_str(&serialized).unwrap();
530
531 assert_eq!(colour, &deserialized);
532 }
533 }
534
535 #[test]
536 fn style_serialization() {
537 let style = Style::default();
538
539 assert_eq!(serde_json::to_string(&style).unwrap(), "{\"foreground\":null,\"background\":null,\"is_bold\":false,\"is_dimmed\":false,\"is_italic\":false,\"is_underline\":false,\"is_blink\":false,\"is_reverse\":false,\"is_hidden\":false,\"is_strikethrough\":false}".to_string());
540 }
541}
542