1 use style::{Colour, Style};
2 
3 use std::fmt;
4 
5 use write::AnyWrite;
6 
7 
8 // ---- generating ANSI codes ----
9 
10 impl Style {
11 
12     /// Write any ANSI codes that go *before* a piece of text. These should be
13     /// the codes to set the terminal to a different colour or font style.
write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>14     fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
15 
16         // If there are actually no styles here, then don’t write *any* codes
17         // as the prefix. An empty ANSI code may not affect the terminal
18         // output at all, but a user may just want a code-free string.
19         if self.is_plain() {
20             return Ok(());
21         }
22 
23         // Write the codes’ prefix, then write numbers, separated by
24         // semicolons, for each text style we want to apply.
25         write!(f, "\x1B[")?;
26         let mut written_anything = false;
27 
28         {
29             let mut write_char = |c| {
30                 if written_anything { write!(f, ";")?; }
31                 written_anything = true;
32                 write!(f, "{}", c)?;
33                 Ok(())
34             };
35 
36             if self.is_bold           { write_char('1')? }
37             if self.is_dimmed         { write_char('2')? }
38             if self.is_italic         { write_char('3')? }
39             if self.is_underline      { write_char('4')? }
40             if self.is_blink          { write_char('5')? }
41             if self.is_reverse        { write_char('7')? }
42             if self.is_hidden         { write_char('8')? }
43             if self.is_strikethrough  { write_char('9')? }
44         }
45 
46         // The foreground and background colours, if specified, need to be
47         // handled specially because the number codes are more complicated.
48         // (see `write_background_code` and `write_foreground_code`)
49         if let Some(bg) = self.background {
50             if written_anything { write!(f, ";")?; }
51             written_anything = true;
52             bg.write_background_code(f)?;
53         }
54 
55         if let Some(fg) = self.foreground {
56             if written_anything { write!(f, ";")?; }
57             fg.write_foreground_code(f)?;
58         }
59 
60         // All the codes end with an `m`, because reasons.
61         write!(f, "m")?;
62 
63         Ok(())
64     }
65 
66     /// Write any ANSI codes that go *after* a piece of text. These should be
67     /// the codes to *reset* the terminal back to its normal colour and style.
write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>68     fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
69         if self.is_plain() {
70             Ok(())
71         }
72         else {
73             write!(f, "{}", RESET)
74         }
75     }
76 }
77 
78 
79 /// The code to send to reset all styles and return to `Style::default()`.
80 pub static RESET: &str = "\x1B[0m";
81 
82 
83 
84 impl Colour {
write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>85     fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
86         match *self {
87             Colour::Black      => write!(f, "30"),
88             Colour::Red        => write!(f, "31"),
89             Colour::Green      => write!(f, "32"),
90             Colour::Yellow     => write!(f, "33"),
91             Colour::Blue       => write!(f, "34"),
92             Colour::Purple     => write!(f, "35"),
93             Colour::Cyan       => write!(f, "36"),
94             Colour::White      => write!(f, "37"),
95             Colour::Fixed(num) => write!(f, "38;5;{}", &num),
96             Colour::RGB(r,g,b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
97         }
98     }
99 
write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>100     fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
101         match *self {
102             Colour::Black      => write!(f, "40"),
103             Colour::Red        => write!(f, "41"),
104             Colour::Green      => write!(f, "42"),
105             Colour::Yellow     => write!(f, "43"),
106             Colour::Blue       => write!(f, "44"),
107             Colour::Purple     => write!(f, "45"),
108             Colour::Cyan       => write!(f, "46"),
109             Colour::White      => write!(f, "47"),
110             Colour::Fixed(num) => write!(f, "48;5;{}", &num),
111             Colour::RGB(r,g,b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
112         }
113     }
114 }
115 
116 
117 /// Like `ANSIString`, but only displays the style prefix.
118 #[derive(Clone, Copy, Debug)]
119 pub struct Prefix(Style);
120 
121 /// Like `ANSIString`, but only displays the difference between two
122 /// styles.
123 #[derive(Clone, Copy, Debug)]
124 pub struct Infix(Style, Style);
125 
126 /// Like `ANSIString`, but only displays the style suffix.
127 #[derive(Clone, Copy, Debug)]
128 pub struct Suffix(Style);
129 
130 
131 impl Style {
132 
133     /// The prefix for this style.
prefix(self) -> Prefix134     pub fn prefix(self) -> Prefix {
135         Prefix(self)
136     }
137 
138     /// The infix between this style and another.
infix(self, other: Style) -> Infix139     pub fn infix(self, other: Style) -> Infix {
140         Infix(self, other)
141     }
142 
143     /// The suffix for this style.
suffix(self) -> Suffix144     pub fn suffix(self) -> Suffix {
145         Suffix(self)
146     }
147 }
148 
149 
150 impl Colour {
151 
152     /// The prefix for this colour.
prefix(self) -> Prefix153     pub fn prefix(self) -> Prefix {
154         Prefix(self.normal())
155     }
156 
157     /// The infix between this colour and another.
infix(self, other: Colour) -> Infix158     pub fn infix(self, other: Colour) -> Infix {
159         Infix(self.normal(), other.normal())
160     }
161 
162     /// The suffix for this colour.
suffix(self) -> Suffix163     pub fn suffix(self) -> Suffix {
164         Suffix(self.normal())
165     }
166 }
167 
168 
169 impl fmt::Display for Prefix {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result170     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171         let f: &mut fmt::Write = f;
172         self.0.write_prefix(f)
173     }
174 }
175 
176 
177 impl fmt::Display for Infix {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result178     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179         use difference::Difference;
180 
181         match Difference::between(&self.0, &self.1) {
182             Difference::ExtraStyles(style) => {
183                 let f: &mut fmt::Write = f;
184                 style.write_prefix(f)
185             },
186             Difference::Reset => {
187                 let f: &mut fmt::Write = f;
188                 write!(f, "{}{}", RESET, self.0.prefix())
189             },
190             Difference::NoDifference => {
191                 Ok(())   // nothing to write
192             },
193         }
194     }
195 }
196 
197 
198 impl fmt::Display for Suffix {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result199     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200         let f: &mut fmt::Write = f;
201         self.0.write_suffix(f)
202     }
203 }
204 
205 
206 
207 #[cfg(test)]
208 mod test {
209     use style::Style;
210     use style::Colour::*;
211 
212     macro_rules! test {
213         ($name: ident: $style: expr; $input: expr => $result: expr) => {
214             #[test]
215             fn $name() {
216                 assert_eq!($style.paint($input).to_string(), $result.to_string());
217 
218                 let mut v = Vec::new();
219                 $style.paint($input.as_bytes()).write_to(&mut v).unwrap();
220                 assert_eq!(v.as_slice(), $result.as_bytes());
221             }
222         };
223     }
224 
225     test!(plain:                 Style::default();                  "text/plain" => "text/plain");
226     test!(red:                   Red;                               "hi" => "\x1B[31mhi\x1B[0m");
227     test!(black:                 Black.normal();                    "hi" => "\x1B[30mhi\x1B[0m");
228     test!(yellow_bold:           Yellow.bold();                     "hi" => "\x1B[1;33mhi\x1B[0m");
229     test!(yellow_bold_2:         Yellow.normal().bold();            "hi" => "\x1B[1;33mhi\x1B[0m");
230     test!(blue_underline:        Blue.underline();                  "hi" => "\x1B[4;34mhi\x1B[0m");
231     test!(green_bold_ul:         Green.bold().underline();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
232     test!(green_bold_ul_2:       Green.underline().bold();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
233     test!(purple_on_white:       Purple.on(White);                  "hi" => "\x1B[47;35mhi\x1B[0m");
234     test!(purple_on_white_2:     Purple.normal().on(White);         "hi" => "\x1B[47;35mhi\x1B[0m");
235     test!(yellow_on_blue:        Style::new().on(Blue).fg(Yellow);  "hi" => "\x1B[44;33mhi\x1B[0m");
236     test!(yellow_on_blue_2:      Cyan.on(Blue).fg(Yellow);          "hi" => "\x1B[44;33mhi\x1B[0m");
237     test!(cyan_bold_on_white:    Cyan.bold().on(White);             "hi" => "\x1B[1;47;36mhi\x1B[0m");
238     test!(cyan_ul_on_white:      Cyan.underline().on(White);        "hi" => "\x1B[4;47;36mhi\x1B[0m");
239     test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
240     test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
241     test!(fixed:                 Fixed(100);                        "hi" => "\x1B[38;5;100mhi\x1B[0m");
242     test!(fixed_on_purple:       Fixed(100).on(Purple);             "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
243     test!(fixed_on_fixed:        Fixed(100).on(Fixed(200));         "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
244     test!(rgb:                   RGB(70,130,180);                   "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
245     test!(rgb_on_blue:           RGB(70,130,180).on(Blue);          "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
246     test!(blue_on_rgb:           Blue.on(RGB(70,130,180));          "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
247     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");
248     test!(bold:                  Style::new().bold();               "hi" => "\x1B[1mhi\x1B[0m");
249     test!(underline:             Style::new().underline();          "hi" => "\x1B[4mhi\x1B[0m");
250     test!(bunderline:            Style::new().bold().underline();   "hi" => "\x1B[1;4mhi\x1B[0m");
251     test!(dimmed:                Style::new().dimmed();             "hi" => "\x1B[2mhi\x1B[0m");
252     test!(italic:                Style::new().italic();             "hi" => "\x1B[3mhi\x1B[0m");
253     test!(blink:                 Style::new().blink();              "hi" => "\x1B[5mhi\x1B[0m");
254     test!(reverse:               Style::new().reverse();            "hi" => "\x1B[7mhi\x1B[0m");
255     test!(hidden:                Style::new().hidden();             "hi" => "\x1B[8mhi\x1B[0m");
256     test!(stricken:              Style::new().strikethrough();      "hi" => "\x1B[9mhi\x1B[0m");
257 
258 }
259