yansi/paint.rs
1use std::fmt;
2
3use style::{Style, Property};
4use color::Color;
5
6/// A structure encapsulating an item and styling.
7///
8/// See the [crate level documentation](./) for usage information.
9///
10/// # Method Glossary
11///
12/// The `Paint` structure exposes many methods for convenience.
13///
14/// ### Unstyled Constructors
15///
16/// Return a new `Paint` structure with no or default styling applied.
17///
18/// * [`Paint::new(item: T)`](Paint::new())
19/// * [`Paint::default(item: T)`](Paint::default())
20/// * [`Paint::masked(item: T)`](Paint::masked())
21/// * [`Paint::wrapping(item: T)`](Paint::wrapping())
22///
23/// ### Foreground Color Constructors
24///
25/// Return a new `Paint` structure with a foreground color applied.
26///
27/// * [`Paint::rgb(r: u8, g: u8, b: u8, item: T)`](Paint::rgb())
28/// * [`Paint::fixed(color: u8, item: T)`](Paint::fixed())
29/// * [`Paint::black(item: T)`](Paint::black())
30/// * [`Paint::red(item: T)`](Paint::red())
31/// * [`Paint::green(item: T)`](Paint::green())
32/// * [`Paint::yellow(item: T)`](Paint::yellow())
33/// * [`Paint::blue(item: T)`](Paint::blue())
34/// * [`Paint::magenta(item: T)`](Paint::magenta())
35/// * [`Paint::cyan(item: T)`](Paint::cyan())
36/// * [`Paint::white(item: T)`](Paint::white())
37///
38/// ### Getters
39///
40/// Return information about the `Paint` structure.
41///
42/// * [`paint.style()`](Paint::style())
43/// * [`paint.inner()`](Paint::inner())
44///
45/// ### Setters
46///
47/// Set a style property on a given `Paint` structure.
48///
49/// * [`paint.with_style(style: Style)`](Paint::with_style())
50/// * [`paint.mask()`](Paint::mask())
51/// * [`paint.wrap()`](Paint::wrap())
52/// * [`paint.fg(color: Color)`](Paint::fg())
53/// * [`paint.bg(color: Color)`](Paint::bg())
54/// * [`paint.bold()`](Paint::bold())
55/// * [`paint.dimmed()`](Paint::dimmed())
56/// * [`paint.italic()`](Paint::italic())
57/// * [`paint.underline()`](Paint::underline())
58/// * [`paint.blink()`](Paint::blink())
59/// * [`paint.invert()`](Paint::invert())
60/// * [`paint.hidden()`](Paint::hidden())
61/// * [`paint.strikethrough()`](Paint::strikethrough())
62///
63/// These methods can be chained:
64///
65/// ```rust
66/// use yansi::Paint;
67///
68/// Paint::new("hi").underline().invert().italic().dimmed().bold();
69/// ```
70///
71/// ### Global Methods
72///
73/// Modify or observe the global behavior of painting.
74///
75/// * [`Paint::enable()`](Paint::enable())
76/// * [`Paint::disable()`](Paint::disable())
77/// * [`Paint::is_enabled()`](Paint::is_enabled())
78/// * [`Paint::enable_windows_ascii()`](Paint::enable_windows_ascii())
79#[derive(Default, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
80pub struct Paint<T> {
81 item: T,
82 style: Style,
83}
84
85macro_rules! constructors_for {
86 ($T:ty, $($name:ident: $color:ident),*) => ($(
87 #[doc = concat!(
88 "Constructs a new `Paint` structure encapsulating `item` with the foreground color\n",
89 "set to ", stringify!($name), ".\n",
90 "```rust\n",
91 "use yansi::Paint;\n",
92 "\n",
93 "println!(\"This is going to be ", stringify!($name),
94 ": {}\", Paint::", stringify!($name), "(\"yay!\"));\n",
95 "```\n"
96 )]
97 #[inline]
98 pub fn $name(item: $T) -> Paint<$T> {
99 Paint::new(item).fg(Color::$color)
100 }
101 )*)
102}
103
104impl<T> Paint<T> {
105 /// Constructs a new `Paint` structure encapsulating `item` with no set
106 /// styling.
107 ///
108 /// ```rust
109 /// use yansi::Paint;
110 ///
111 /// assert_eq!(Paint::new("hello!").to_string(), "hello!".to_string());
112 /// ```
113 #[inline]
114 pub fn new(item: T) -> Paint<T> {
115 Paint { item, style: Style::default() }
116 }
117
118 /// Constructs a new `Paint` structure encapsulating `item` with the active
119 /// terminal's default foreground and background.
120 ///
121 /// ```rust
122 /// use yansi::Paint;
123 ///
124 /// println!("This is going to use {}!", Paint::default("default colors"));
125 /// ```
126 #[inline]
127 pub fn default(item: T) -> Paint<T> {
128 Paint::new(item).fg(Color::Default).bg(Color::Default)
129 }
130
131 /// Constructs a new _masked_ `Paint` structure encapsulating `item` with
132 /// no set styling.
133 ///
134 /// A masked `Paint` is not written out when painting is disabled during
135 /// `Display` or `Debug` invocations. When painting is enabled, masking has
136 /// no effect.
137 ///
138 /// ```rust
139 /// use yansi::Paint;
140 ///
141 /// // The emoji won't be printed when coloring is disabled.
142 /// println!("{}Sprout!", Paint::masked("🌱 "));
143 /// ```
144 #[inline]
145 pub fn masked(item: T) -> Paint<T> {
146 Paint::new(item).mask()
147 }
148
149 /// Constructs a new _wrapping_ `Paint` structure encapsulating `item` with
150 /// default styling.
151 ///
152 /// A wrapping `Paint` converts all color resets written out by the internal
153 /// value to the styling of itself. This allows for seamless color wrapping
154 /// of other colored text.
155 ///
156 /// # Performance
157 ///
158 /// In order to wrap an internal value, the internal value must first be
159 /// written out to a local buffer and examined. As a result, displaying a
160 /// wrapped value is likely to result in a heap allocation and copy.
161 ///
162 /// # Example
163 ///
164 /// ```rust
165 /// use yansi::{Paint, Color};
166 ///
167 /// let inner = format!("{} and {}", Paint::red("Stop"), Paint::green("Go"));
168 ///
169 /// // 'Hey!' will be unstyled, "Stop" will be red, "and" will be blue, and
170 /// // "Go" will be green. Without a wrapping `Paint`, "and" would be
171 /// // unstyled.
172 /// println!("Hey! {}", Paint::wrapping(inner).fg(Color::Blue));
173 /// ```
174 #[inline]
175 pub fn wrapping(item: T) -> Paint<T> {
176 Paint::new(item).wrap()
177 }
178
179 /// Constructs a new `Paint` structure encapsulating `item` with the
180 /// foreground color set to the RGB color `r`, `g`, `b`.
181 ///
182 /// ```rust
183 /// use yansi::Paint;
184 ///
185 /// println!("This is going to be funky: {}", Paint::rgb(70, 130, 122, "hi!"));
186 /// ```
187 #[inline]
188 pub fn rgb(r: u8, g: u8, b: u8, item: T) -> Paint<T> {
189 Paint::new(item).fg(Color::RGB(r, g, b))
190 }
191
192 /// Constructs a new `Paint` structure encapsulating `item` with the
193 /// foreground color set to the fixed 8-bit color `color`.
194 ///
195 /// ```rust
196 /// use yansi::Paint;
197 ///
198 /// println!("This is going to be funky: {}", Paint::fixed(100, "hi!"));
199 /// ```
200 #[inline]
201 pub fn fixed(color: u8, item: T) -> Paint<T> {
202 Paint::new(item).fg(Color::Fixed(color))
203 }
204
205 constructors_for!(T, black: Black, red: Red, green: Green, yellow: Yellow,
206 blue: Blue, magenta: Magenta, cyan: Cyan, white: White);
207
208 /// Retrieves the style currently set on `self`.
209 ///
210 /// ```rust
211 /// use yansi::{Style, Color, Paint};
212 ///
213 /// let alert = Style::new(Color::Red).bold().underline();
214 /// let painted = Paint::red("hi").bold().underline();
215 ///
216 /// assert_eq!(alert, painted.style());
217 /// ```
218 #[inline]
219 pub fn style(&self) -> Style {
220 self.style
221 }
222
223 /// Retrieves a borrow to the inner item.
224 ///
225 /// ```rust
226 /// use yansi::Paint;
227 ///
228 /// let x = Paint::red("Hello, world!");
229 /// assert_eq!(*x.inner(), "Hello, world!");
230 /// ```
231 #[inline]
232 pub fn inner(&self) -> &T {
233 &self.item
234 }
235
236 /// Sets the style of `self` to `style`.
237 ///
238 /// Any styling currently set on `self` is lost. Prefer to use the
239 /// [`style.paint()`](Style::paint()) method to create a `Paint` struct from
240 /// `Style`.
241 ///
242 /// ```rust
243 /// use yansi::{Paint, Color, Style};
244 ///
245 /// let s = Style::new(Color::Red).bold().underline();
246 ///
247 /// // Using this method.
248 /// println!("Alert: {}", Paint::new("This thing happened!").with_style(s));
249 ///
250 /// // Using the `style.paint()` method.
251 /// println!("Alert: {}", s.paint("This thing happened!"));
252 /// ```
253 #[inline]
254 pub fn with_style(mut self, style: Style) -> Paint<T> {
255 self.style = style;
256 self
257 }
258
259 /// Masks `self`.
260 ///
261 /// A masked `Paint` is not written out when painting is disabled during
262 /// `Display` or `Debug` invocations. When painting is enabled, masking has
263 /// no effect.
264 ///
265 /// ```rust
266 /// use yansi::Paint;
267 ///
268 /// // "Whoops! " will only print when coloring is enabled.
269 /// println!("{}Something happened.", Paint::red("Whoops! ").mask());
270 /// ```
271 #[inline]
272 pub fn mask(mut self) -> Paint<T> {
273 self.style.masked = true;
274 self
275 }
276
277 /// Makes `self` a _wrapping_ `Paint`.
278 ///
279 /// A wrapping `Paint` converts all color resets written out by the internal
280 /// value to the styling of itself. This allows for seamless color wrapping
281 /// of other colored text.
282 ///
283 /// # Performance
284 ///
285 /// In order to wrap an internal value, the internal value must first be
286 /// written out to a local buffer and examined. As a result, displaying a
287 /// wrapped value is likely to result in a heap allocation and copy.
288 ///
289 /// # Example
290 ///
291 /// ```rust
292 /// use yansi::{Paint, Color};
293 ///
294 /// let inner = format!("{} and {}", Paint::red("Stop"), Paint::green("Go"));
295 ///
296 /// // 'Hey!' will be unstyled, "Stop" will be red, "and" will be blue, and
297 /// // "Go" will be green. Without a wrapping `Paint`, "and" would be
298 /// // unstyled.
299 /// println!("Hey! {}", Paint::blue(inner).wrap());
300 /// ```
301 #[inline]
302 pub fn wrap(mut self) -> Paint<T> {
303 self.style.wrap = true;
304 self
305 }
306
307 /// Sets the foreground to `color`.
308 ///
309 /// ```rust
310 /// use yansi::Paint;
311 /// use yansi::Color::Red;
312 ///
313 /// println!("Red foreground: {}", Paint::new("hi!").fg(Red));
314 /// ```
315 #[inline]
316 pub fn fg(mut self, color: Color) -> Paint<T> {
317 self.style.foreground = color;
318 self
319 }
320
321 /// Sets the background to `color`.
322 ///
323 /// ```rust
324 /// use yansi::Paint;
325 /// use yansi::Color::Yellow;
326 ///
327 /// println!("Yellow background: {}", Paint::new("hi!").bg(Yellow));
328 /// ```
329 #[inline]
330 pub fn bg(mut self, color: Color) -> Paint<T> {
331 self.style.background = color;
332 self
333 }
334
335 style_builder_for!(Paint<T>, |paint| paint.style.properties,
336 bold: BOLD, dimmed: DIMMED, italic: ITALIC,
337 underline: UNDERLINE, blink: BLINK, invert: INVERT,
338 hidden: HIDDEN, strikethrough: STRIKETHROUGH);
339}
340
341macro_rules! impl_fmt_trait {
342 ($trait:ident, $fmt:expr) => (
343 impl<T: fmt::$trait> fmt::$trait for Paint<T> {
344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345 if Paint::is_enabled() && self.style.wrap {
346 let mut prefix = String::new();
347 prefix.push_str("\x1B[0m");
348 self.style.fmt_prefix(&mut prefix)?;
349
350 self.style.fmt_prefix(f)?;
351 let item = format!($fmt, self.item).replace("\x1B[0m", &prefix);
352 fmt::$trait::fmt(&item, f)?;
353 self.style.fmt_suffix(f)
354 } else if Paint::is_enabled() {
355 self.style.fmt_prefix(f)?;
356 fmt::$trait::fmt(&self.item, f)?;
357 self.style.fmt_suffix(f)
358 } else if !self.style.masked {
359 fmt::$trait::fmt(&self.item, f)
360 } else {
361 Ok(())
362 }
363 }
364 }
365 )
366}
367
368impl_fmt_trait!(Display, "{}");
369impl_fmt_trait!(Debug, "{:?}");
370
371use std::sync::atomic::AtomicBool;
372use std::sync::atomic::Ordering;
373
374static ENABLED: AtomicBool = AtomicBool::new(true);
375
376impl Paint<()> {
377 /// Disables coloring globally.
378 ///
379 /// # Example
380 ///
381 /// ```rust
382 /// use yansi::Paint;
383 ///
384 /// // With coloring enabled, ANSI color codes are emitted.
385 /// assert_ne!(Paint::green("go").to_string(), "go".to_string());
386 ///
387 /// // With coloring disabled, ANSI color codes are _not_ emitted.
388 /// Paint::disable();
389 /// assert_eq!(Paint::green("go").to_string(), "go".to_string());
390 /// ```
391 pub fn disable() {
392 ENABLED.store(false, Ordering::Release);
393 }
394
395 /// Enables coloring globally. Coloring is enabled by default, so this
396 /// method should only be called to _re_ enable coloring.
397 ///
398 /// # Example
399 ///
400 /// ```rust
401 /// use yansi::Paint;
402 ///
403 /// // With coloring disabled, ANSI color codes are _not_ emitted.
404 /// Paint::disable();
405 /// assert_eq!(Paint::green("go").to_string(), "go".to_string());
406 ///
407 /// // Reenabling causes color code to be emitted.
408 /// Paint::enable();
409 /// assert_ne!(Paint::green("go").to_string(), "go".to_string());
410 /// ```
411 pub fn enable() {
412 ENABLED.store(true, Ordering::Release);
413 }
414
415 /// Returns `true` if coloring is enabled and `false` otherwise. Coloring is
416 /// enabled by default but can be enabled and disabled on-the-fly with the
417 /// [`Paint::enable()`] and [`Paint::disable()`] methods.
418 ///
419 /// [`Paint::disable()`]: struct.Paint.html#method.disable
420 /// [`Paint::enable()`]: struct.Paint.html#method.disable
421 ///
422 /// # Example
423 ///
424 /// ```rust
425 /// use yansi::Paint;
426 ///
427 /// // Coloring is enabled by default.
428 /// assert!(Paint::is_enabled());
429 ///
430 /// // Disable it with `Paint::disable()`.
431 /// Paint::disable();
432 /// assert!(!Paint::is_enabled());
433 ///
434 /// // Reenable with `Paint::enable()`.
435 /// Paint::enable();
436 /// assert!(Paint::is_enabled());
437 /// ```
438 pub fn is_enabled() -> bool {
439 ENABLED.load(Ordering::Acquire)
440 }
441
442 /// Enables ASCII terminal escape sequences on Windows consoles when
443 /// possible. Returns `true` if escape sequence support was successfully
444 /// enabled and `false` otherwise. On non-Windows targets, this method
445 /// always returns `true`.
446 ///
447 /// Support for escape sequences in Windows consoles was added in the
448 /// Windows 10 anniversary update. For targets with older Windows
449 /// installations, this method is expected to return `false`.
450 ///
451 /// # Example
452 ///
453 /// ```rust
454 /// use yansi::Paint;
455 ///
456 /// // A best-effort Windows ASCII terminal support enabling.
457 /// Paint::enable_windows_ascii();
458 /// ```
459 #[inline]
460 pub fn enable_windows_ascii() -> bool {
461 ::windows::enable_ascii_colors()
462 }
463}