requestty_ui/
style.rs

1//! A module to control the looks of text.
2
3use std::{fmt::Display, io};
4
5/// Some content with a particular style applied.
6///
7/// See also [`write_styled`] and [`Stylize`].
8///
9/// [`write_styled`]: crate::backend::Backend::write_styled
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct Styled<T: ?Sized> {
12    fg: Option<Color>,
13    bg: Option<Color>,
14    attributes: Attributes,
15    content: T,
16}
17
18impl<T: Display> Styled<T> {
19    /// Creates a new [`Styled`]
20    pub fn new(content: T) -> Self {
21        Self {
22            fg: None,
23            bg: None,
24            attributes: Attributes::empty(),
25            content,
26        }
27    }
28}
29
30impl<T: Display + ?Sized> Styled<T> {
31    pub(super) fn write<B: crate::backend::Backend + ?Sized>(
32        &self,
33        backend: &mut B,
34    ) -> io::Result<()> {
35        if let Some(fg) = self.fg {
36            backend.set_fg(fg)?;
37        }
38        if let Some(bg) = self.bg {
39            backend.set_bg(bg)?;
40        }
41        if !self.attributes.is_empty() {
42            backend.set_attributes(self.attributes)?;
43        }
44
45        write!(backend, "{}", &self.content)?;
46
47        if self.fg.is_some() {
48            backend.set_fg(Color::Reset)?;
49        }
50        if self.bg.is_some() {
51            backend.set_bg(Color::Reset)?;
52        }
53        if !self.attributes.is_empty() {
54            backend.set_attributes(Attributes::empty())?;
55        }
56        Ok(())
57    }
58}
59
60impl<T: Display> From<T> for Styled<T> {
61    fn from(content: T) -> Self {
62        Self::new(content)
63    }
64}
65
66/// Represents a color. See the underlying terminal library documentation for information on
67/// terminal compatibility.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69#[allow(missing_docs)]
70pub enum Color {
71    Reset,
72    Black,
73    Red,
74    Green,
75    Yellow,
76    Blue,
77    Magenta,
78    Cyan,
79    Grey,
80    DarkGrey,
81    LightRed,
82    LightGreen,
83    LightYellow,
84    LightBlue,
85    LightMagenta,
86    LightCyan,
87    White,
88    Rgb(u8, u8, u8),
89    Ansi(u8),
90}
91
92bitflags::bitflags! {
93    /// Attributes change the way a piece of text is displayed.
94    pub struct Attributes: u16 {
95        /// Increases the text intensity.
96        const BOLD              = 0b0000_0000_0001;
97        /// Decreases the text intensity.
98        const DIM               = 0b0000_0000_0010;
99        /// Emphasises the text.
100        const ITALIC            = 0b0000_0000_0100;
101        /// Underlines the text.
102        const UNDERLINED        = 0b0000_0000_1000;
103        /// Makes the text blinking (< 150 per minute).
104        const SLOW_BLINK        = 0b0000_0001_0000;
105        /// Makes the text blinking (>= 150 per minute).
106        const RAPID_BLINK       = 0b0000_0010_0000;
107        /// Swaps foreground and background colors.
108        const REVERSED          = 0b0000_0100_0000;
109        /// Hides the text (also known as Conceal).
110        const HIDDEN            = 0b0000_1000_0000;
111        /// Crosses the text.
112        const CROSSED_OUT       = 0b0001_0000_0000;
113    }
114}
115
116impl Attributes {
117    /// Gets the attribute diff that needs to be applied to transition from the attributes in `self`
118    /// to the attributes in `to`.
119    pub fn diff(self, to: Attributes) -> AttributeDiff {
120        let diff = self ^ to;
121        AttributeDiff {
122            to_remove: diff & self,
123            to_add: diff & to,
124        }
125    }
126}
127
128/// The change in attributes. Use [`Attributes::diff`] to create one.
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
130#[allow(missing_docs)]
131pub struct AttributeDiff {
132    pub to_add: Attributes,
133    pub to_remove: Attributes,
134}
135
136/// Provides a set of methods to set the colors and attributes.
137///
138/// Every method with the `on_` prefix sets the background color. Other color methods set the
139/// foreground color.
140///
141/// Method names correspond to the [`Color`] enum variants and [`Attributes`] names.
142///
143/// See also [`Styled`] and [`write_styled`].
144///
145/// [`write_styled`]: crate::backend::Backend::write_styled
146#[allow(missing_docs)]
147pub trait Stylize<T> {
148    fn black(self) -> Styled<T>;
149    fn dark_grey(self) -> Styled<T>;
150    fn light_red(self) -> Styled<T>;
151    fn red(self) -> Styled<T>;
152    fn light_green(self) -> Styled<T>;
153    fn green(self) -> Styled<T>;
154    fn light_yellow(self) -> Styled<T>;
155    fn yellow(self) -> Styled<T>;
156    fn light_blue(self) -> Styled<T>;
157    fn blue(self) -> Styled<T>;
158    fn light_magenta(self) -> Styled<T>;
159    fn magenta(self) -> Styled<T>;
160    fn light_cyan(self) -> Styled<T>;
161    fn cyan(self) -> Styled<T>;
162    fn white(self) -> Styled<T>;
163    fn grey(self) -> Styled<T>;
164    fn rgb(self, r: u8, g: u8, b: u8) -> Styled<T>;
165    fn ansi(self, ansi: u8) -> Styled<T>;
166
167    fn on_black(self) -> Styled<T>;
168    fn on_dark_grey(self) -> Styled<T>;
169    fn on_light_red(self) -> Styled<T>;
170    fn on_red(self) -> Styled<T>;
171    fn on_light_green(self) -> Styled<T>;
172    fn on_green(self) -> Styled<T>;
173    fn on_light_yellow(self) -> Styled<T>;
174    fn on_yellow(self) -> Styled<T>;
175    fn on_light_blue(self) -> Styled<T>;
176    fn on_blue(self) -> Styled<T>;
177    fn on_light_magenta(self) -> Styled<T>;
178    fn on_magenta(self) -> Styled<T>;
179    fn on_light_cyan(self) -> Styled<T>;
180    fn on_cyan(self) -> Styled<T>;
181    fn on_white(self) -> Styled<T>;
182    fn on_grey(self) -> Styled<T>;
183    fn on_rgb(self, r: u8, g: u8, b: u8) -> Styled<T>;
184    fn on_ansi(self, ansi: u8) -> Styled<T>;
185
186    fn bold(self) -> Styled<T>;
187    fn underlined(self) -> Styled<T>;
188    fn reverse(self) -> Styled<T>;
189    fn dim(self) -> Styled<T>;
190    fn italic(self) -> Styled<T>;
191    fn slow_blink(self) -> Styled<T>;
192    fn rapid_blink(self) -> Styled<T>;
193    fn hidden(self) -> Styled<T>;
194    fn crossed_out(self) -> Styled<T>;
195}
196
197impl<T, I: Into<Styled<T>>> Stylize<T> for I {
198    fn black(self) -> Styled<T> {
199        let mut styled = self.into();
200        styled.fg = Some(Color::Black);
201        styled
202    }
203    fn dark_grey(self) -> Styled<T> {
204        let mut styled = self.into();
205        styled.fg = Some(Color::DarkGrey);
206        styled
207    }
208    fn light_red(self) -> Styled<T> {
209        let mut styled = self.into();
210        styled.fg = Some(Color::LightRed);
211        styled
212    }
213    fn red(self) -> Styled<T> {
214        let mut styled = self.into();
215        styled.fg = Some(Color::Red);
216        styled
217    }
218    fn light_green(self) -> Styled<T> {
219        let mut styled = self.into();
220        styled.fg = Some(Color::LightGreen);
221        styled
222    }
223    fn green(self) -> Styled<T> {
224        let mut styled = self.into();
225        styled.fg = Some(Color::Green);
226        styled
227    }
228    fn light_yellow(self) -> Styled<T> {
229        let mut styled = self.into();
230        styled.fg = Some(Color::LightYellow);
231        styled
232    }
233    fn yellow(self) -> Styled<T> {
234        let mut styled = self.into();
235        styled.fg = Some(Color::Yellow);
236        styled
237    }
238    fn light_blue(self) -> Styled<T> {
239        let mut styled = self.into();
240        styled.fg = Some(Color::LightBlue);
241        styled
242    }
243    fn blue(self) -> Styled<T> {
244        let mut styled = self.into();
245        styled.fg = Some(Color::Blue);
246        styled
247    }
248    fn light_magenta(self) -> Styled<T> {
249        let mut styled = self.into();
250        styled.fg = Some(Color::LightMagenta);
251        styled
252    }
253    fn magenta(self) -> Styled<T> {
254        let mut styled = self.into();
255        styled.fg = Some(Color::Magenta);
256        styled
257    }
258    fn light_cyan(self) -> Styled<T> {
259        let mut styled = self.into();
260        styled.fg = Some(Color::LightCyan);
261        styled
262    }
263    fn cyan(self) -> Styled<T> {
264        let mut styled = self.into();
265        styled.fg = Some(Color::Cyan);
266        styled
267    }
268    fn white(self) -> Styled<T> {
269        let mut styled = self.into();
270        styled.fg = Some(Color::White);
271        styled
272    }
273    fn grey(self) -> Styled<T> {
274        let mut styled = self.into();
275        styled.fg = Some(Color::Grey);
276        styled
277    }
278    fn rgb(self, r: u8, g: u8, b: u8) -> Styled<T> {
279        let mut styled = self.into();
280        styled.fg = Some(Color::Rgb(r, g, b));
281        styled
282    }
283    fn ansi(self, ansi: u8) -> Styled<T> {
284        let mut styled = self.into();
285        styled.fg = Some(Color::Ansi(ansi));
286        styled
287    }
288
289    fn on_black(self) -> Styled<T> {
290        let mut styled = self.into();
291        styled.bg = Some(Color::Black);
292        styled
293    }
294    fn on_dark_grey(self) -> Styled<T> {
295        let mut styled = self.into();
296        styled.bg = Some(Color::DarkGrey);
297        styled
298    }
299    fn on_light_red(self) -> Styled<T> {
300        let mut styled = self.into();
301        styled.bg = Some(Color::LightRed);
302        styled
303    }
304    fn on_red(self) -> Styled<T> {
305        let mut styled = self.into();
306        styled.bg = Some(Color::Red);
307        styled
308    }
309    fn on_light_green(self) -> Styled<T> {
310        let mut styled = self.into();
311        styled.bg = Some(Color::LightGreen);
312        styled
313    }
314    fn on_green(self) -> Styled<T> {
315        let mut styled = self.into();
316        styled.bg = Some(Color::Green);
317        styled
318    }
319    fn on_light_yellow(self) -> Styled<T> {
320        let mut styled = self.into();
321        styled.bg = Some(Color::LightYellow);
322        styled
323    }
324    fn on_yellow(self) -> Styled<T> {
325        let mut styled = self.into();
326        styled.bg = Some(Color::Yellow);
327        styled
328    }
329    fn on_light_blue(self) -> Styled<T> {
330        let mut styled = self.into();
331        styled.bg = Some(Color::LightBlue);
332        styled
333    }
334    fn on_blue(self) -> Styled<T> {
335        let mut styled = self.into();
336        styled.bg = Some(Color::Blue);
337        styled
338    }
339    fn on_light_magenta(self) -> Styled<T> {
340        let mut styled = self.into();
341        styled.bg = Some(Color::LightMagenta);
342        styled
343    }
344    fn on_magenta(self) -> Styled<T> {
345        let mut styled = self.into();
346        styled.bg = Some(Color::Magenta);
347        styled
348    }
349    fn on_light_cyan(self) -> Styled<T> {
350        let mut styled = self.into();
351        styled.bg = Some(Color::LightCyan);
352        styled
353    }
354    fn on_cyan(self) -> Styled<T> {
355        let mut styled = self.into();
356        styled.bg = Some(Color::Cyan);
357        styled
358    }
359    fn on_white(self) -> Styled<T> {
360        let mut styled = self.into();
361        styled.bg = Some(Color::White);
362        styled
363    }
364    fn on_grey(self) -> Styled<T> {
365        let mut styled = self.into();
366        styled.bg = Some(Color::Grey);
367        styled
368    }
369    fn on_rgb(self, r: u8, g: u8, b: u8) -> Styled<T> {
370        let mut styled = self.into();
371        styled.bg = Some(Color::Rgb(r, g, b));
372        styled
373    }
374    fn on_ansi(self, ansi: u8) -> Styled<T> {
375        let mut styled = self.into();
376        styled.bg = Some(Color::Ansi(ansi));
377        styled
378    }
379
380    fn bold(self) -> Styled<T> {
381        let mut styled = self.into();
382        styled.attributes |= Attributes::BOLD;
383        styled
384    }
385    fn underlined(self) -> Styled<T> {
386        let mut styled = self.into();
387        styled.attributes |= Attributes::UNDERLINED;
388        styled
389    }
390    fn reverse(self) -> Styled<T> {
391        let mut styled = self.into();
392        styled.attributes |= Attributes::REVERSED;
393        styled
394    }
395    fn dim(self) -> Styled<T> {
396        let mut styled = self.into();
397        styled.attributes |= Attributes::DIM;
398        styled
399    }
400    fn italic(self) -> Styled<T> {
401        let mut styled = self.into();
402        styled.attributes |= Attributes::ITALIC;
403        styled
404    }
405    fn slow_blink(self) -> Styled<T> {
406        let mut styled = self.into();
407        styled.attributes |= Attributes::SLOW_BLINK;
408        styled
409    }
410    fn rapid_blink(self) -> Styled<T> {
411        let mut styled = self.into();
412        styled.attributes |= Attributes::RAPID_BLINK;
413        styled
414    }
415    fn hidden(self) -> Styled<T> {
416        let mut styled = self.into();
417        styled.attributes |= Attributes::HIDDEN;
418        styled
419    }
420    fn crossed_out(self) -> Styled<T> {
421        let mut styled = self.into();
422        styled.attributes |= Attributes::CROSSED_OUT;
423        styled
424    }
425}