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