Coverage Report

Created: 2026-05-30 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/yansi-1.0.1/src/style.rs
Line
Count
Source
1
use core::fmt::{self, Write};
2
3
use crate::color::{Color, Variant};
4
use crate::attr_quirk::{Attribute, Quirk};
5
use crate::condition::Condition;
6
use crate::set::Set;
7
8
#[cfg(all(feature = "alloc", not(feature = "std")))]
9
use alloc::{string::String, borrow::Cow};
10
11
#[cfg(feature = "std")]
12
use std::borrow::Cow;
13
14
/// A set of styling options.
15
///
16
/// ## Equivalence and Ordering
17
///
18
/// Only a style's `foreground`, `background`, and set of `attributes` are
19
/// considered when testing for equivalence or producing an ordering via
20
/// `PartialEq` or `Eq`, and `PartialOrd` or `Ord`. A style's quirks and
21
/// conditions are ignored.
22
#[derive(Default, Debug, Copy, Clone)]
23
pub struct Style {
24
    /// The foreground color. Defaults to `None`.
25
    ///
26
    /// ```rust
27
    /// use yansi::{Style, Color};
28
    ///
29
    /// assert_eq!(Style::new().foreground, None);
30
    /// assert_eq!(Style::new().green().foreground, Some(Color::Green));
31
    /// ```
32
    pub foreground: Option<Color>,
33
    /// The background color. Defaults to `None`.
34
    ///
35
    /// ```rust
36
    /// use yansi::{Style, Color};
37
    ///
38
    /// assert_eq!(Style::new().background, None);
39
    /// assert_eq!(Style::new().on_red().background, Some(Color::Red));
40
    /// ```
41
    pub background: Option<Color>,
42
    pub(crate) attributes: Set<Attribute>,
43
    pub(crate) quirks: Set<Quirk>,
44
    /// The condition.
45
    ///
46
    /// To check a style's condition directly, use [`Style::enabled()`]:
47
    ///
48
    /// ```rust
49
    /// use yansi::{Style, Condition};
50
    ///
51
    /// let style = Style::new().whenever(Condition::ALWAYS);
52
    /// assert!(style.enabled());
53
    ///
54
    /// let style = Style::new().whenever(Condition::NEVER);
55
    /// assert!(!style.enabled());
56
    /// ```
57
    pub condition: Option<Condition>,
58
}
59
60
struct AnsiSplicer<'a> {
61
    f: &'a mut dyn fmt::Write,
62
    splice: bool,
63
}
64
65
#[derive(Debug)]
66
#[allow(non_camel_case_types)]
67
pub enum Application {
68
    fg(Color),
69
    bg(Color),
70
    attr(Attribute),
71
    quirk(Quirk),
72
    whenever(Condition),
73
}
74
75
impl Style {
76
    const DEFAULT: Style = Style {
77
        foreground: None,
78
        background: None,
79
        attributes: Set::EMPTY,
80
        quirks: Set::EMPTY,
81
        condition: None,
82
    };
83
84
    /// Returns a new style with no foreground or background, no attributes
85
    /// or quirks, and [`Condition::DEFAULT`].
86
    ///
87
    /// This is the default returned by [`Default::default()`].
88
    ///
89
    /// # Example
90
    ///
91
    /// ```rust
92
    /// use yansi::Style;
93
    ///
94
    /// assert_eq!(Style::new(), Style::default());
95
    /// ```
96
    #[inline]
97
0
    pub const fn new() -> Style {
98
0
        Style::DEFAULT
99
0
    }
Unexecuted instantiation: <yansi::style::Style>::new
Unexecuted instantiation: <yansi::style::Style>::new
Unexecuted instantiation: <yansi::style::Style>::new
100
101
    #[inline(always)]
102
0
    pub(crate) const fn apply(mut self, a: Application) -> Style {
103
0
        match a {
104
0
            Application::fg(color) => self.foreground = Some(color),
105
0
            Application::bg(color) => self.background = Some(color),
106
0
            Application::whenever(cond) => self.condition = Some(cond),
107
0
            Application::attr(attr) => self.attributes = self.attributes.insert(attr),
108
0
            Application::quirk(quirk) => self.quirks = self.quirks.insert(quirk),
109
        }
110
111
0
        self
112
0
    }
113
114
    /// Returns `true` if this style is enabled, based on
115
    /// [`condition`](Paint.condition).
116
    ///
117
    /// **Note:** _For a style to be effected, both this method **and**
118
    /// [`yansi::is_enabled()`](crate::is_enabled) must return `true`._
119
    ///
120
    /// When there is no condition set, this method always returns `true`. When
121
    /// a condition has been set, this evaluates the condition and returns the
122
    /// result.
123
    ///
124
    /// # Example
125
    ///
126
    /// ```rust
127
    /// use yansi::{Style, Condition};
128
    ///
129
    /// let style = Style::new().whenever(Condition::ALWAYS);
130
    /// assert!(style.enabled());
131
    ///
132
    /// let style = Style::new().whenever(Condition::NEVER);
133
    /// assert!(!style.enabled());
134
    /// ```
135
0
    pub fn enabled(&self) -> bool {
136
0
        self.condition.map_or(true, |c| c())
137
0
    }
138
139
    /// Writes the ANSI code prefix for the currently set styles.
140
    ///
141
    /// This method is intended to be used inside of [`fmt::Display`] and
142
    /// [`fmt::Debug`] implementations for custom or specialized use-cases. Most
143
    /// users should use [`Painted`] for all painting needs.
144
    ///
145
    /// This method writes the ANSI code prefix irrespective of whether painting
146
    /// is currently enabled or disabled. To write the prefix only if painting
147
    /// is enabled, condition a call to this method on [`is_enabled()`].
148
    ///
149
    /// [`fmt::Display`]: fmt::Display
150
    /// [`fmt::Debug`]: fmt::Debug
151
    /// [`Painted`]: crate::Painted
152
    /// [`is_enabled()`]: crate::is_enabled()
153
    ///
154
    /// # Example
155
    ///
156
    /// ```rust
157
    /// use core::fmt;
158
    /// use yansi::Style;
159
    ///
160
    /// struct CustomItem {
161
    ///     item: u32,
162
    ///     style: Style
163
    /// }
164
    ///
165
    /// impl fmt::Display for CustomItem {
166
    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167
    ///         self.style.fmt_prefix(f)?;
168
    ///         write!(f, "number: {}", self.item)?;
169
    ///         self.style.fmt_suffix(f)
170
    ///     }
171
    /// }
172
    /// ```
173
0
    pub fn fmt_prefix(&self, f: &mut dyn fmt::Write) -> fmt::Result {
174
        // Give a sequence-free string when no styles are applied.
175
0
        if self == &Style::DEFAULT {
176
0
            return Ok(());
177
0
        }
178
179
0
        let brighten = |color: Option<Color>, bright: bool| match (color, bright) {
180
0
            (Some(color), true) => Some(color.to_bright()),
181
0
            _ => color
182
0
        };
183
184
0
        let mut f = AnsiSplicer { f, splice: false };
185
0
        f.write_str("\x1B[")?;
186
187
0
        for attr in self.attributes.iter() {
188
0
            f.splice()?;
189
0
            attr.fmt(&mut f)?;
190
        }
191
192
0
        if let Some(color) = brighten(self.background, self.quirks.contains(Quirk::OnBright)) {
193
0
            f.splice()?;
194
0
            color.fmt(&mut f, Variant::Bg)?;
195
0
        }
196
197
0
        if let Some(color) = brighten(self.foreground, self.quirks.contains(Quirk::Bright)) {
198
0
            f.splice()?;
199
0
            color.fmt(&mut f, Variant::Fg)?;
200
0
        }
201
202
        // All of the sequences end with an `m`.
203
0
        f.write_char('m')
204
0
    }
205
206
    /// Returns the ANSI code sequence prefix for the style as a string.
207
    ///
208
    /// This returns a string with the exact same sequence written by
209
    /// [`fmt_prefix()`](Self::fmt_prefix()). See that method for details.
210
    #[cfg(feature = "alloc")]
211
    #[cfg_attr(feature = "_nightly", doc(cfg(feature = "alloc")))]
212
0
    pub fn prefix(&self) -> Cow<'static, str> {
213
0
        let mut prefix = String::new();
214
0
        let _ = self.fmt_prefix(&mut prefix);
215
0
        prefix.into()
216
0
    }
217
218
    /// Writes the ANSI code sequence suffix for the style.
219
    ///
220
    /// This method is intended to be used inside of [`fmt::Display`] and
221
    /// [`fmt::Debug`] implementations for custom or specialized use-cases. Most
222
    /// users should use [`Painted`] for all painting needs.
223
    ///
224
    /// This method writes the ANSI code suffix irrespective of whether painting
225
    /// is currently enabled or disabled. To write the suffix only if painting
226
    /// is enabled, condition a call to this method on [`is_enabled()`].
227
    ///
228
    /// [`fmt::Display`]: fmt::Display
229
    /// [`fmt::Debug`]: fmt::Debug
230
    /// [`Painted`]: crate::Painted
231
    /// [`is_enabled()`]: crate::is_enabled()
232
    ///
233
    /// # Example
234
    ///
235
    /// ```rust
236
    /// use core::fmt;
237
    /// use yansi::Style;
238
    ///
239
    /// struct CustomItem {
240
    ///     item: u32,
241
    ///     style: Style
242
    /// }
243
    ///
244
    /// impl fmt::Display for CustomItem {
245
    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246
    ///         self.style.fmt_prefix(f)?;
247
    ///         write!(f, "number: {}", self.item)?;
248
    ///         self.style.fmt_suffix(f)
249
    ///     }
250
    /// }
251
    /// ```
252
0
    pub fn fmt_suffix(&self, f: &mut dyn fmt::Write) -> fmt::Result {
253
0
        if !self.quirks.contains(Quirk::Resetting) && !self.quirks.contains(Quirk::Clear) {
254
0
            if self.quirks.contains(Quirk::Linger) || self == &Style::DEFAULT {
255
0
                return Ok(());
256
0
            }
257
0
        }
258
259
0
        f.write_str("\x1B[0m")
260
0
    }
261
262
    /// Returns the ANSI code sequence suffix for the style as a string.
263
    ///
264
    /// This returns a string with the exact same sequence written by
265
    /// [`fmt_suffix()`](Self::fmt_suffix()). See that method for details.
266
    #[cfg(feature = "alloc")]
267
    #[cfg_attr(feature = "_nightly", doc(cfg(feature = "alloc")))]
268
0
    pub fn suffix(&self) -> Cow<'static, str> {
269
0
        if !self.quirks.contains(Quirk::Resetting) && !self.quirks.contains(Quirk::Clear) {
270
0
            if self.quirks.contains(Quirk::Linger) || self == &Style::DEFAULT {
271
0
                return Cow::from("");
272
0
            }
273
0
        }
274
275
0
        Cow::from("\x1B[0m")
276
0
    }
277
278
    properties!([pub const] constructor(Self) -> Self);
279
}
280
281
impl AnsiSplicer<'_> {
282
0
    fn splice(&mut self) -> fmt::Result {
283
0
        if self.splice { self.f.write_char(';')?; }
284
0
        self.splice = true;
285
0
        Ok(())
286
0
    }
287
}
288
289
impl fmt::Write for AnsiSplicer<'_> {
290
0
    fn write_str(&mut self, s: &str) -> fmt::Result {
291
0
        self.f.write_str(s)
292
0
    }
293
}
294
295
impl PartialEq for Style {
296
0
    fn eq(&self, other: &Self) -> bool {
297
        let Style {
298
0
            foreground: fg_a,
299
0
            background: bg_a,
300
0
            attributes: attrs_a,
301
            quirks: _,
302
            condition: _,
303
0
        } = self;
304
305
        let Style {
306
0
            foreground: fg_b,
307
0
            background: bg_b,
308
0
            attributes: attrs_b,
309
            quirks: _,
310
            condition: _,
311
0
        } = other;
312
313
0
        fg_a == fg_b && bg_a == bg_b && attrs_a == attrs_b
314
0
    }
315
}
316
317
impl Eq for Style { }
318
319
impl core::hash::Hash for Style {
320
0
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
321
0
        let Style { foreground, background, attributes, quirks: _, condition: _, } = self;
322
0
        foreground.hash(state);
323
0
        background.hash(state);
324
0
        attributes.hash(state);
325
0
    }
326
}
327
328
impl PartialOrd for Style {
329
0
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
330
        let Style {
331
0
            foreground: fg_a,
332
0
            background: bg_a,
333
0
            attributes: attrs_a,
334
            quirks: _,
335
            condition: _,
336
0
        } = self;
337
338
        let Style {
339
0
            foreground: fg_b,
340
0
            background: bg_b,
341
0
            attributes: attrs_b,
342
            quirks: _,
343
            condition: _,
344
0
        } = other;
345
346
0
        match fg_a.partial_cmp(&fg_b) {
347
0
            Some(core::cmp::Ordering::Equal) => {}
348
0
            ord => return ord,
349
        }
350
351
0
        match bg_a.partial_cmp(&bg_b) {
352
0
            Some(core::cmp::Ordering::Equal) => {}
353
0
            ord => return ord,
354
        }
355
356
0
        attrs_a.partial_cmp(&attrs_b)
357
0
    }
358
}
359
360
impl Ord for Style {
361
0
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
362
        let Style {
363
0
            foreground: fg_a,
364
0
            background: bg_a,
365
0
            attributes: attrs_a,
366
            quirks: _,
367
            condition: _,
368
0
        } = self;
369
370
        let Style {
371
0
            foreground: fg_b,
372
0
            background: bg_b,
373
0
            attributes: attrs_b,
374
            quirks: _,
375
            condition: _,
376
0
        } = other;
377
378
0
        match fg_a.cmp(&fg_b) {
379
0
            core::cmp::Ordering::Equal => {}
380
0
            ord => return ord,
381
        }
382
383
0
        match bg_a.cmp(&bg_b) {
384
0
            core::cmp::Ordering::Equal => {}
385
0
            ord => return ord,
386
        }
387
388
0
        attrs_a.cmp(&attrs_b)
389
0
    }
390
}