yansi/
style.rs

1use std::hash::{Hash, Hasher};
2use std::fmt::{self, Display};
3use std::ops::BitOr;
4
5use {Paint, Color};
6
7#[derive(Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
8pub struct Property(u8);
9
10impl Property {
11    pub const BOLD: Self = Property(1 << 0);
12    pub const DIMMED: Self = Property(1 << 1);
13    pub const ITALIC: Self = Property(1 << 2);
14    pub const UNDERLINE: Self = Property(1 << 3);
15    pub const BLINK: Self = Property(1 << 4);
16    pub const INVERT: Self = Property(1 << 5);
17    pub const HIDDEN: Self = Property(1 << 6);
18    pub const STRIKETHROUGH: Self = Property(1 << 7);
19
20    #[inline(always)]
21    pub fn contains(self, other: Property) -> bool {
22        (other.0 & self.0) == other.0
23    }
24
25    #[inline(always)]
26    pub fn set(&mut self, other: Property) {
27        self.0 |= other.0;
28    }
29
30    #[inline(always)]
31    pub fn iter(self) -> Iter {
32        Iter { index: 0, properties: self }
33    }
34}
35
36impl BitOr for Property {
37    type Output = Self;
38
39    #[inline(always)]
40    fn bitor(self, rhs: Self) -> Self {
41        Property(self.0 | rhs.0)
42    }
43}
44
45pub struct Iter {
46    index: u8,
47    properties: Property,
48}
49
50impl Iterator for Iter {
51    type Item = usize;
52
53    fn next(&mut self) -> Option<Self::Item> {
54        while self.index < 8 {
55            let index = self.index;
56            self.index += 1;
57
58            if self.properties.contains(Property(1 << index)) {
59                return Some(index as usize);
60            }
61        }
62
63        None
64    }
65}
66
67/// Represents a set of styling options.
68///
69/// See the [crate level documentation](./) for usage information.
70///
71/// # Method Glossary
72///
73/// The `Style` structure exposes many methods for convenience. The majority of
74/// these methods are shared with [`Paint`](Paint).
75///
76/// ### Foreground Color Constructors
77///
78/// Return a new `Style` structure with a foreground `color` applied.
79///
80///   * [`Style::new(color: Color)`](Style::new())
81///
82/// ### Setters
83///
84/// Set a style property on a given `Style` structure.
85///
86///   * [`style.fg(color: Color)`](Style::fg())
87///   * [`style.bg(color: Color)`](Style::bg())
88///   * [`style.mask()`](Style::mask())
89///   * [`style.wrap()`](Style::wrap())
90///   * [`style.bold()`](Style::bold())
91///   * [`style.dimmed()`](Style::dimmed())
92///   * [`style.italic()`](Style::italic())
93///   * [`style.underline()`](Style::underline())
94///   * [`style.blink()`](Style::blink())
95///   * [`style.invert()`](Style::invert())
96///   * [`style.hidden()`](Style::hidden())
97///   * [`style.strikethrough()`](Style::strikethrough())
98///
99/// These methods can be chained:
100///
101/// ```rust
102/// use yansi::{Style, Color::{Red, Magenta}};
103///
104/// Style::new(Red).bg(Magenta).underline().invert().italic().dimmed().bold();
105/// ```
106///
107/// ### Converters
108///
109/// Convert a `Style` into another structure.
110///
111///   * [`style.paint<T>(item: T) -> Paint<T>`](Style::paint())
112///
113/// ### Getters
114///
115/// Return information about a `Style` structure.
116///
117///   * [`style.fg_color()`](Style::fg_color())
118///   * [`style.bg_color()`](Style::bg_color())
119///   * [`style.is_masked()`](Style::is_masked())
120///   * [`style.is_wrapping()`](Style::is_wrapping())
121///   * [`style.is_bold()`](Style::is_bold())
122///   * [`style.is_dimmed()`](Style::is_dimmed())
123///   * [`style.is_italic()`](Style::is_italic())
124///   * [`style.is_underline()`](Style::is_underline())
125///   * [`style.is_blink()`](Style::is_blink())
126///   * [`style.is_invert()`](Style::is_invert())
127///   * [`style.is_hidden()`](Style::is_hidden())
128///   * [`style.is_strikethrough()`](Style::is_strikethrough())
129///
130/// ### Raw Formatters
131///
132/// Write the raw ANSI codes for a given `Style` to any `fmt::Write`.
133///
134///   * [`style.fmt_prefix(f: &mut fmt::Write)`](Style::fmt_prefix())
135///   * [`style.fmt_suffix(f: &mut fmt::Write)`](Style::fmt_suffix())
136#[repr(packed)]
137#[derive(Default, Debug, Eq, Ord, PartialOrd, Copy, Clone)]
138pub struct Style {
139    pub(crate) foreground: Color,
140    pub(crate) background: Color,
141    pub(crate) properties: Property,
142    pub(crate) masked: bool,
143    pub(crate) wrap: bool,
144}
145
146impl PartialEq for Style {
147    fn eq(&self, other: &Style) -> bool {
148        self.foreground == other.foreground
149            && self.background == other.background
150            && self.properties == other.properties
151    }
152}
153
154impl Hash for Style {
155    fn hash<H: Hasher>(&self, state: &mut H) {
156        self.foreground.hash(state);
157        self.background.hash(state);
158        self.properties.hash(state);
159    }
160}
161
162macro_rules! checker_for {
163    ($($name:ident ($fn_name:ident): $property:ident),*) => ($(
164        #[doc = concat!(
165            "Returns `true` if the _", stringify!($name), "_ property is set on `self`.\n",
166            "```rust\n",
167            "use yansi::Style;\n",
168            "\n",
169            "let plain = Style::default();\n",
170            "assert!(!plain.", stringify!($fn_name), "());\n",
171            "\n",
172            "let styled = plain.", stringify!($name), "();\n",
173            "assert!(styled.", stringify!($fn_name), "());\n",
174            "```\n"
175        )]
176        #[inline]
177        pub fn $fn_name(&self) -> bool {
178            self.properties.contains(Property::$property)
179        }
180    )*)
181}
182
183#[inline]
184fn write_spliced<T: Display>(c: &mut bool, f: &mut fmt::Write, t: T) -> fmt::Result {
185    if *c {
186        write!(f, ";{}", t)
187    } else {
188        *c = true;
189        write!(f, "{}", t)
190    }
191}
192
193impl Style {
194    /// Default style with the foreground set to `color` and no other set
195    /// properties.
196    ///
197    /// ```rust
198    /// use yansi::Style;
199    ///
200    /// let plain = Style::default();
201    /// assert_eq!(plain, Style::default());
202    /// ```
203    #[inline]
204    pub fn new(color: Color) -> Style {
205        Self::default().fg(color)
206    }
207
208    /// Sets the foreground to `color`.
209    ///
210    /// ```rust
211    /// use yansi::{Color, Style};
212    ///
213    /// let red_fg = Style::default().fg(Color::Red);
214    /// ```
215    #[inline]
216    pub fn fg(mut self, color: Color) -> Style {
217        self.foreground = color;
218        self
219    }
220
221    /// Sets the background to `color`.
222    ///
223    /// ```rust
224    /// use yansi::{Color, Style};
225    ///
226    /// let red_bg = Style::default().bg(Color::Red);
227    /// ```
228    #[inline]
229    pub fn bg(mut self, color: Color) -> Style {
230        self.background = color;
231        self
232    }
233
234    /// Sets `self` to be masked.
235    ///
236    /// An item with _masked_ styling is not written out when painting is
237    /// disabled during `Display` or `Debug` invocations. When painting is
238    /// enabled, masking has no effect.
239    ///
240    /// ```rust
241    /// use yansi::Style;
242    ///
243    /// let masked = Style::default().mask();
244    ///
245    /// // "Whoops! " will only print when coloring is enabled.
246    /// println!("{}Something happened.", masked.paint("Whoops! "));
247    /// ```
248    #[inline]
249    pub fn mask(mut self) -> Style {
250        self.masked = true;
251        self
252    }
253
254    /// Sets `self` to be wrapping.
255    ///
256    /// A wrapping `Style` converts all color resets written out by the internal
257    /// value to the styling of itself. This allows for seamless color wrapping
258    /// of other colored text.
259    ///
260    /// # Performance
261    ///
262    /// In order to wrap an internal value, the internal value must first be
263    /// written out to a local buffer and examined. As a result, displaying a
264    /// wrapped value is likely to result in a heap allocation and copy.
265    ///
266    /// ```rust
267    /// use yansi::{Paint, Style, Color};
268    ///
269    /// let inner = format!("{} and {}", Paint::red("Stop"), Paint::green("Go"));
270    /// let wrapping = Style::new(Color::Blue).wrap();
271    ///
272    /// // 'Hey!' will be unstyled, "Stop" will be red, "and" will be blue, and
273    /// // "Go" will be green. Without a wrapping `Paint`, "and" would be
274    /// // unstyled.
275    /// println!("Hey! {}", wrapping.paint(inner));
276    /// ```
277    #[inline]
278    pub fn wrap(mut self) -> Style {
279        self.wrap = true;
280        self
281    }
282
283    style_builder_for!(Style, |style| style.properties,
284                       bold: BOLD, dimmed: DIMMED, italic: ITALIC,
285                       underline: UNDERLINE, blink: BLINK, invert: INVERT,
286                       hidden: HIDDEN, strikethrough: STRIKETHROUGH);
287
288    /// Constructs a new `Paint` structure that encapsulates `item` with the
289    /// style set to `self`.
290    ///
291    /// ```rust
292    /// use yansi::{Style, Color};
293    ///
294    /// let alert = Style::new(Color::Red).bold().underline();
295    /// println!("Alert: {}", alert.paint("This thing happened!"));
296    /// ```
297    #[inline]
298    pub fn paint<T>(self, item: T) -> Paint<T> {
299        Paint::new(item).with_style(self)
300    }
301
302    /// Returns the foreground color of `self`.
303    ///
304    /// ```rust
305    /// use yansi::{Style, Color};
306    ///
307    /// let plain = Style::default();
308    /// assert_eq!(plain.fg_color(), Color::Unset);
309    ///
310    /// let red = plain.fg(Color::Red);
311    /// assert_eq!(red.fg_color(), Color::Red);
312    /// ```
313    #[inline]
314    pub fn fg_color(&self) -> Color {
315        self.foreground
316    }
317
318    /// Returns the foreground color of `self`.
319    ///
320    /// ```rust
321    /// use yansi::{Style, Color};
322    ///
323    /// let plain = Style::default();
324    /// assert_eq!(plain.bg_color(), Color::Unset);
325    ///
326    /// let white = plain.bg(Color::White);
327    /// assert_eq!(white.bg_color(), Color::White);
328    /// ```
329    #[inline]
330    pub fn bg_color(&self) -> Color {
331        self.background
332    }
333
334    /// Returns `true` if `self` is masked.
335    ///
336    /// ```rust
337    /// use yansi::Style;
338    ///
339    /// let plain = Style::default();
340    /// assert!(!plain.is_masked());
341    ///
342    /// let masked = plain.mask();
343    /// assert!(masked.is_masked());
344    /// ```
345    #[inline]
346    pub fn is_masked(&self) -> bool {
347        self.masked
348    }
349
350    /// Returns `true` if `self` is wrapping.
351    ///
352    /// ```rust
353    /// use yansi::Style;
354    ///
355    /// let plain = Style::default();
356    /// assert!(!plain.is_wrapping());
357    ///
358    /// let wrapping = plain.wrap();
359    /// assert!(wrapping.is_wrapping());
360    /// ```
361    #[inline]
362    pub fn is_wrapping(&self) -> bool {
363        self.wrap
364    }
365
366    checker_for!(bold (is_bold): BOLD, dimmed (is_dimmed): DIMMED,
367        italic (is_italic): ITALIC, underline (is_underline): UNDERLINE,
368        blink (is_blink): BLINK, invert (is_invert): INVERT,
369        hidden (is_hidden): HIDDEN,
370        strikethrough (is_strikethrough): STRIKETHROUGH);
371
372    #[inline(always)]
373    fn is_plain(&self) -> bool {
374        self == &Style::default()
375    }
376
377    /// Writes the ANSI code prefix for the currently set styles.
378    ///
379    /// This method is intended to be used inside of [`fmt::Display`] and
380    /// [`fmt::Debug`] implementations for custom or specialized use-cases. Most
381    /// users should use [`Paint`] for all painting needs.
382    ///
383    /// This method writes the ANSI code prefix irrespective of whether painting
384    /// is currently enabled or disabled. To write the prefix only if painting
385    /// is enabled, condition a call to this method on [`Paint::is_enabled()`].
386    ///
387    /// [`fmt::Display`]: fmt::Display
388    /// [`fmt::Debug`]: fmt::Debug
389    /// [`Paint`]: Paint
390    /// [`Paint::is_enabled()`]: Paint::is_enabled()
391    ///
392    /// # Example
393    ///
394    /// ```rust
395    /// use std::fmt;
396    /// use yansi::Style;
397    ///
398    /// struct CustomItem {
399    ///     item: u32,
400    ///     style: Style
401    /// }
402    ///
403    /// impl fmt::Display for CustomItem {
404    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405    ///         self.style.fmt_prefix(f)?;
406    ///         write!(f, "number: {}", self.item)?;
407    ///         self.style.fmt_suffix(f)
408    ///     }
409    /// }
410    /// ```
411    pub fn fmt_prefix(&self, f: &mut fmt::Write) -> fmt::Result {
412        // A user may just want a code-free string when no styles are applied.
413        if self.is_plain() {
414            return Ok(());
415        }
416
417        let mut splice = false;
418        write!(f, "\x1B[")?;
419
420        for i in self.properties.iter() {
421            let k = if i >= 5 { i + 2 } else { i + 1 };
422            write_spliced(&mut splice, f, k)?;
423        }
424
425        if self.background != Color::Unset {
426            write_spliced(&mut splice, f, "4")?;
427            self.background.ascii_fmt(f)?;
428        }
429
430        if self.foreground != Color::Unset {
431            write_spliced(&mut splice, f, "3")?;
432            self.foreground.ascii_fmt(f)?;
433        }
434
435        // All the codes end with an `m`.
436        write!(f, "m")
437    }
438
439    /// Writes the ANSI code suffix for the currently set styles.
440    ///
441    /// This method is intended to be used inside of [`fmt::Display`] and
442    /// [`fmt::Debug`] implementations for custom or specialized use-cases. Most
443    /// users should use [`Paint`] for all painting needs.
444    ///
445    /// This method writes the ANSI code suffix irrespective of whether painting
446    /// is currently enabled or disabled. To write the suffix only if painting
447    /// is enabled, condition a call to this method on [`Paint::is_enabled()`].
448    ///
449    /// [`fmt::Display`]: fmt::Display
450    /// [`fmt::Debug`]: fmt::Debug
451    /// [`Paint`]: Paint
452    /// [`Paint::is_enabled()`]: Paint::is_enabled()
453    ///
454    /// # Example
455    ///
456    /// ```rust
457    /// use std::fmt;
458    /// use yansi::Style;
459    ///
460    /// struct CustomItem {
461    ///     item: u32,
462    ///     style: Style
463    /// }
464    ///
465    /// impl fmt::Display for CustomItem {
466    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
467    ///         self.style.fmt_prefix(f)?;
468    ///         write!(f, "number: {}", self.item)?;
469    ///         self.style.fmt_suffix(f)
470    ///     }
471    /// }
472    /// ```
473    pub fn fmt_suffix(&self, f: &mut fmt::Write) -> fmt::Result {
474        if self.is_plain() {
475            return Ok(());
476        }
477
478        write!(f, "\x1B[0m")
479    }
480}