Coverage Report

Created: 2025-08-26 06:41

/rust/registry/src/index.crates.io-6f17d22bba15001f/yansi-1.0.1/src/paint.rs
Line
Count
Source (jump to first uncovered line)
1
use core::fmt;
2
3
#[cfg(all(feature = "alloc", not(feature = "std")))]
4
use alloc::{string::{String, ToString}, borrow::Cow};
5
6
#[cfg(feature = "std")]
7
use std::borrow::Cow;
8
9
use crate::{Color, Attribute, Quirk, Style, Condition};
10
11
/// An arbitrary value with a [`Style`] applied to it.
12
///
13
/// A `Painted` can be directly formatted. This results in the internal
14
/// [`value`](Self::value) being formatted as specified and ANSI code styling
15
/// sequences corresponding to [`style`](Self::style) being prefixed and
16
/// suffixed as necessary. Both the global and local [`Condition`] affects
17
/// whether styling sequences are actually emitted: both must evaluated to true.
18
/// Otherwise, no styling sequences are emitted.
19
///
20
/// ```rust
21
/// use yansi::{Paint, Condition};
22
///
23
/// println!("Hello, {}!", "world".red().underline().blink());
24
/// // > Hello, world! # world is red, underlined, and blinking
25
///
26
/// let v = format!("{}", "world".red().underline().blink());
27
/// assert_eq!(v, "\u{1b}[4;5;31mworld\u{1b}[0m");
28
/// println!("{}", v); // > world # world is red, underlined, and blinking
29
///
30
/// let v = format!("{}", "world".red().underline().blink().whenever(Condition::NEVER));
31
/// assert_eq!(v, "world");
32
/// ```
33
#[derive(Copy, Clone)]
34
pub struct Painted<T> {
35
    /// The value to be styled.
36
    pub value: T,
37
    /// The style to apply.
38
    pub style: Style,
39
}
40
41
/// A trait to apply styling to any value. Implemented for all types.
42
///
43
/// Because this trait is implemented for all types, you can use its methods on
44
/// any type. With the exception of one constructor method, [`Paint::new()`],
45
/// all methods are called with method syntax:
46
///
47
/// ```rust
48
/// use yansi::Paint;
49
///
50
/// "hello".green(); // calls `Paint::<&'static str>::green()`.
51
/// "hello".strike(); // calls `Paint::<&'static str>::strike()`.
52
/// 1.on_red(); // calls `Paint::<i32>::red()`.
53
/// 1.blink(); // calls `Paint::<i32>::blink()`.
54
/// ```
55
///
56
/// ### Chaining
57
///
58
/// All methods return a [`Painted`] whose methods are exactly those of `Paint`.
59
/// This means you can chain `Paint` method calls:
60
///
61
/// ```rust
62
/// use yansi::Paint;
63
///
64
/// "hello".green().strike(); // calls `Paint::green()` then `Painted::strike()`.
65
/// 1.on_red().blink(); // calls `Paint::red()` + `Painted::blink()`.
66
/// ```
67
///
68
/// ### Borrow vs. Owned Receiver
69
///
70
/// The returned [`Painted`] type contains a borrow to the receiver:
71
///
72
/// ```rust
73
/// use yansi::{Paint, Painted};
74
///
75
/// let v: Painted<&i32> = 1.red();
76
/// ```
77
///
78
/// This is nearly always what you want. In the exceedingly rare case that you
79
/// _do_ want `Painted` to own its value, use [`Paint::new()`] or the equivalent
80
/// [`Painted::new()`]:
81
///
82
/// ```rust
83
/// use yansi::{Paint, Painted};
84
///
85
/// let v: Painted<i32> = Paint::new(1);
86
/// let v: Painted<i32> = Painted::new(1);
87
/// ```
88
///
89
/// ### Further Details
90
///
91
/// See the [crate level docs](crate#usage) for more details and examples.
92
pub trait Paint {
93
    /// Create a new [`Painted`] with a default [`Style`].
94
    ///
95
    /// # Example
96
    ///
97
    /// ```rust
98
    /// use yansi::Paint;
99
    ///
100
    /// let painted = Paint::new("hello");
101
    /// assert_eq!(painted.style, yansi::Style::new());
102
    /// ```
103
    #[inline(always)]
104
0
    fn new(self) -> Painted<Self> where Self: Sized {
105
0
        Painted::new(self)
106
0
    }
107
108
    #[doc(hidden)]
109
    #[inline(always)]
110
0
    fn apply(&self, a: crate::style::Application) -> Painted<&Self> {
111
0
        Painted::new(self).apply(a)
112
0
    }
Unexecuted instantiation: <char as yansi::paint::Paint>::apply
Unexecuted instantiation: <str as yansi::paint::Paint>::apply
Unexecuted instantiation: <_ as yansi::paint::Paint>::apply
113
114
    /// Apply a style wholesale to `self`. Any previous style is replaced.
115
    ///
116
    /// # Example
117
    ///
118
    /// ```rust
119
    /// use yansi::{Paint, Style, Color::*};
120
    ///
121
    /// static DEBUG: Style = Black.bold().on_yellow();
122
    ///
123
    /// let painted = "hello".paint(DEBUG);
124
    /// ```
125
    #[inline(always)]
126
0
    fn paint<S: Into<Style>>(&self, style: S) -> Painted<&Self> {
127
0
        Painted { value: self, style: style.into() }
128
0
    }
Unexecuted instantiation: <alloc::string::String as yansi::paint::Paint>::paint::<yansi::color::Color>
Unexecuted instantiation: <_ as yansi::paint::Paint>::paint::<_>
129
130
    properties!(signature(&Self) -> Painted<&Self>);
131
}
132
133
#[allow(rustdoc::broken_intra_doc_links)]
134
impl<T: ?Sized> Paint for T {
135
    properties!(constructor(&Self) -> Painted<&Self>);
136
}
137
138
impl<T> Painted<T> {
139
    /// Create a new [`Painted`] with a default [`Style`].
140
    ///
141
    /// # Example
142
    ///
143
    /// ```rust
144
    /// use yansi::Painted;
145
    ///
146
    /// let painted = Painted::new("hello");
147
    /// assert_eq!(painted.style, yansi::Style::new());
148
    /// ```
149
    #[inline(always)]
150
0
    pub const fn new(value: T) -> Painted<T> {
151
0
        Painted { value, style: Style::new() }
152
0
    }
Unexecuted instantiation: <yansi::paint::Painted<&char>>::new
Unexecuted instantiation: <yansi::paint::Painted<&str>>::new
Unexecuted instantiation: <yansi::paint::Painted<_>>::new
153
154
    #[inline(always)]
155
0
    const fn apply(mut self, a: crate::style::Application) -> Self {
156
0
        self.style = self.style.apply(a);
157
0
        self
158
0
    }
Unexecuted instantiation: <yansi::paint::Painted<&char>>::apply
Unexecuted instantiation: <yansi::paint::Painted<&str>>::apply
Unexecuted instantiation: <yansi::paint::Painted<_>>::apply
159
160
    #[inline]
161
0
    pub(crate) fn enabled(&self) -> bool {
162
0
        crate::is_enabled() && self.style.condition.map_or(true, |c| c())
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::enabled::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<&char>>::enabled::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<&str>>::enabled::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<_>>::enabled::{closure#0}
163
0
    }
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::enabled
Unexecuted instantiation: <yansi::paint::Painted<&char>>::enabled
Unexecuted instantiation: <yansi::paint::Painted<&str>>::enabled
Unexecuted instantiation: <yansi::paint::Painted<_>>::enabled
164
165
    properties!([pub const] constructor(Self) -> Self);
166
}
167
168
impl<T> Painted<T> {
169
0
    pub(crate) fn color_fmt_value(
170
0
        &self,
171
0
        fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
172
0
        f: &mut fmt::Formatter,
173
0
    ) -> fmt::Result {
174
0
        self.style.fmt_prefix(f)?;
175
0
        fmt(&self.value, f)?;
176
0
        self.style.fmt_suffix(f)
177
0
    }
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::color_fmt_value
Unexecuted instantiation: <yansi::paint::Painted<&char>>::color_fmt_value
Unexecuted instantiation: <yansi::paint::Painted<&str>>::color_fmt_value
Unexecuted instantiation: <yansi::paint::Painted<_>>::color_fmt_value
178
179
    #[cfg(feature = "alloc")]
180
0
    pub(crate) fn reset_fmt_args(
181
0
        &self,
182
0
        fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
183
0
        f: &mut fmt::Formatter,
184
0
        args: &fmt::Arguments<'_>,
185
0
    ) -> fmt::Result {
186
        // A tiny state machine to find escape sequences.
187
        enum State { Searching, Open, }
188
0
        let mut state = State::Searching;
189
0
        let escape = |c: char| {
190
0
            match state {
191
0
                State::Searching if c == '\x1B' => { state = State::Open; true }
192
0
                State::Open if c == 'm' => { state = State::Searching; true }
193
0
                State::Searching => false,
194
0
                State::Open => true,
195
            }
196
0
        };
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::reset_fmt_args::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<&char>>::reset_fmt_args::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<&str>>::reset_fmt_args::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<_>>::reset_fmt_args::{closure#0}
197
198
        // Only replace when the string contains styling.
199
0
        let string = args.as_str()
200
0
            .map(|string| Cow::Borrowed(string))
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::reset_fmt_args::{closure#1}
Unexecuted instantiation: <yansi::paint::Painted<&char>>::reset_fmt_args::{closure#1}
Unexecuted instantiation: <yansi::paint::Painted<&str>>::reset_fmt_args::{closure#1}
Unexecuted instantiation: <yansi::paint::Painted<_>>::reset_fmt_args::{closure#1}
201
0
            .unwrap_or_else(|| Cow::Owned(args.to_string()));
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::reset_fmt_args::{closure#2}
Unexecuted instantiation: <yansi::paint::Painted<&char>>::reset_fmt_args::{closure#2}
Unexecuted instantiation: <yansi::paint::Painted<&str>>::reset_fmt_args::{closure#2}
Unexecuted instantiation: <yansi::paint::Painted<_>>::reset_fmt_args::{closure#2}
202
0
203
0
        if string.contains('\x1B') {
204
0
            f.write_str(&string.replace(escape, ""))
205
        } else {
206
0
            fmt(&self.value, f)
207
        }
208
0
    }
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::reset_fmt_args
Unexecuted instantiation: <yansi::paint::Painted<&char>>::reset_fmt_args
Unexecuted instantiation: <yansi::paint::Painted<&str>>::reset_fmt_args
Unexecuted instantiation: <yansi::paint::Painted<_>>::reset_fmt_args
209
210
    #[cfg(feature = "alloc")]
211
0
    pub(crate) fn color_wrap_fmt_args(
212
0
        &self,
213
0
        fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
214
0
        f: &mut fmt::Formatter,
215
0
        args: &fmt::Arguments<'_>,
216
0
    ) -> fmt::Result {
217
0
        // Only replace when the string contains styling.
218
0
        let string = args.as_str()
219
0
            .map(|string| Cow::Borrowed(string))
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::color_wrap_fmt_args::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<&char>>::color_wrap_fmt_args::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<&str>>::color_wrap_fmt_args::{closure#0}
Unexecuted instantiation: <yansi::paint::Painted<_>>::color_wrap_fmt_args::{closure#0}
220
0
            .unwrap_or_else(|| Cow::Owned(args.to_string()));
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::color_wrap_fmt_args::{closure#1}
Unexecuted instantiation: <yansi::paint::Painted<&char>>::color_wrap_fmt_args::{closure#1}
Unexecuted instantiation: <yansi::paint::Painted<&str>>::color_wrap_fmt_args::{closure#1}
Unexecuted instantiation: <yansi::paint::Painted<_>>::color_wrap_fmt_args::{closure#1}
221
0
222
0
        if !string.contains('\x1B') {
223
0
            return self.color_fmt_value(fmt, f);
224
0
        }
225
0
226
0
        // Compute the prefix for the style with a reset in front.
227
0
        let mut prefix = String::new();
228
0
        prefix.push_str("\x1B[0m");
229
0
        self.style.fmt_prefix(&mut prefix)?;
230
231
        // Write out formatted string, replacing resets with computed prefix.
232
0
        self.style.fmt_prefix(f)?;
233
0
        write!(f, "{}", string.replace("\x1B[0m", &prefix))?;
234
0
        self.style.fmt_suffix(f)
235
0
    }
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::color_wrap_fmt_args
Unexecuted instantiation: <yansi::paint::Painted<&char>>::color_wrap_fmt_args
Unexecuted instantiation: <yansi::paint::Painted<&str>>::color_wrap_fmt_args
Unexecuted instantiation: <yansi::paint::Painted<_>>::color_wrap_fmt_args
236
237
0
    pub(crate) fn fmt_args(
238
0
        &self,
239
0
        fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
240
0
        f: &mut fmt::Formatter,
241
0
        _args: fmt::Arguments<'_>,
242
0
    ) -> fmt::Result {
243
0
        let enabled = self.enabled();
244
0
        let masked = self.style.quirks.contains(Quirk::Mask);
245
0
246
0
        #[cfg(not(feature = "alloc"))]
247
0
        match (enabled, masked) {
248
0
            (true, _) => self.color_fmt_value(fmt, f),
249
0
            (false, false) => fmt(&self.value, f),
250
0
            (false, true) => Ok(()),
251
0
        }
252
0
253
0
        #[cfg(feature = "alloc")]
254
0
        match (enabled, masked, self.style.quirks.contains(Quirk::Wrap)) {
255
0
            (true, _, true) => self.color_wrap_fmt_args(fmt, f, &_args),
256
0
            (true, _, false) => self.color_fmt_value(fmt, f),
257
0
            (false, false, true) => self.reset_fmt_args(fmt, f, &_args),
258
0
            (false, false, false) => fmt(&self.value, f),
259
0
            (false, true, _) => Ok(()),
260
        }
261
0
    }
Unexecuted instantiation: <yansi::paint::Painted<&alloc::string::String>>::fmt_args
Unexecuted instantiation: <yansi::paint::Painted<&char>>::fmt_args
Unexecuted instantiation: <yansi::paint::Painted<&str>>::fmt_args
Unexecuted instantiation: <yansi::paint::Painted<_>>::fmt_args
262
}
263
264
impl_fmt_traits!(<T> Painted<T> => self.value (T));
265
266
impl<T> From<Painted<T>> for Style {
267
0
    fn from(painted: Painted<T>) -> Self {
268
0
        painted.style
269
0
    }
270
}