/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 | | } |