stylish_style/
lib.rs

1#![no_std]
2
3//! Internal implementation details of [`stylish-core`](https://docs.rs/stylish-core).
4//!
5//! Do not depend on this crate directly.
6
7#![doc(test(attr(deny(warnings))))]
8#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12
13/// A color that can be used with [`Foreground`] to modify [`Style::foreground`]
14/// or [`Background`] to modify [`Style::background`].
15#[allow(dead_code)]
16#[derive(Copy, Clone, Debug, Eq, PartialEq)]
17#[non_exhaustive]
18pub enum Color {
19    /// Black
20    Black,
21    /// Red
22    Red,
23    /// Green
24    Green,
25    /// Yellow
26    Yellow,
27    /// Blue
28    Blue,
29    /// Magenta
30    Magenta,
31    /// Cyan
32    Cyan,
33    /// White
34    White,
35    // Bright Black
36    BrightBlack,
37    // Bright Red
38    BrightRed,
39    // Bright Green
40    BrightGreen,
41    // Bright Yellow
42    BrightYellow,
43    // Bright Blue
44    BrightBlue,
45    // Bright Magenta
46    BrightMagenta,
47    // Bright Cyan
48    BrightCyan,
49    // Bright White
50    BrightWhite,
51    /// Default color
52    Default,
53}
54
55/// An intensity to render text with, to emphasise or de-emphasise it as needed.
56#[derive(Copy, Clone, Debug, Eq, PartialEq)]
57#[non_exhaustive]
58pub enum Intensity {
59    /// The normal intensity
60    Normal,
61    /// A bolder intensity to emphasise content
62    Bold,
63    /// A fainter intensity to de-emphasise content
64    Faint,
65}
66
67/// A style to render text with, setting the foreground and background colors,
68/// along with intensity.
69#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
70#[non_exhaustive]
71pub struct Style {
72    /// The text foreground color
73    pub foreground: Color,
74    /// The text background color
75    pub background: Color,
76    /// The text intensity
77    pub intensity: Intensity,
78}
79
80/// A diff between two styles.
81///
82/// Most useful for some implementors of `stylish::Write` to detect changes
83/// between two parts, or for applying multiple changes to a style at once.
84#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
85#[non_exhaustive]
86pub struct StyleDiff {
87    /// The change in the text foreground color
88    pub foreground: Option<Color>,
89    /// The change in the text background color
90    pub background: Option<Color>,
91    /// The change in the text intensity
92    pub intensity: Option<Intensity>,
93}
94
95/// A [`Restyle`] implementor for setting [`Style::foreground`].
96///
97/// ```rust
98/// use stylish::{Color, Foreground, Style};
99///
100/// let mut expected = Style::default();
101/// expected.foreground = Color::Magenta;
102///
103/// assert_eq!(Style::default().with(Foreground(Color::Magenta)), expected);
104/// ```
105#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
106pub struct Foreground(pub Color);
107
108/// A [`Restyle`] implementor for setting [`Style::background`].
109///
110/// ```rust
111/// use stylish::{Background, Color, Style};
112///
113/// let mut expected = Style::default();
114/// expected.background = Color::Magenta;
115///
116/// assert_eq!(Style::default().with(Background(Color::Magenta)), expected);
117/// ```
118#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
119pub struct Background(pub Color);
120
121/// A trait for modifications to [`Style`], allowing an ergonomic API with
122/// [`Style::with`] and `stylish::Formatter::with`.
123///
124/// ```rust
125/// use stylish::{Color, Foreground, Intensity, Restyle, Style};
126///
127/// struct OhNo;
128///
129/// impl Restyle for OhNo {
130///     fn apply(&self, style: Style) -> Style {
131///         style.with(Foreground(Color::Red)).with(Intensity::Bold)
132///     }
133/// }
134///
135/// let mut expected = Style::default();
136/// expected.foreground = Color::Red;
137/// expected.intensity = Intensity::Bold;
138///
139/// assert_eq!(Style::default().with(OhNo), expected);
140/// ```
141pub trait Restyle {
142    /// Apply the restyling this instance represents to an existing style,
143    /// returning the updated style.
144    fn apply(&self, style: Style) -> Style;
145}
146
147impl Default for Color {
148    #[inline]
149    fn default() -> Self {
150        Self::Default
151    }
152}
153
154impl Default for Intensity {
155    #[inline]
156    fn default() -> Self {
157        Self::Normal
158    }
159}
160
161impl Style {
162    /// Apply a modification to this style, returning the result.
163    ///
164    /// ```rust
165    /// use stylish::{Intensity, Style};
166    ///
167    /// let mut expected = Style::default();
168    /// expected.intensity = Intensity::Faint;
169    ///
170    /// assert_eq!(Style::default().with(Intensity::Faint), expected);
171    /// ```
172    pub fn with(self, adj: impl Restyle) -> Self {
173        adj.apply(self)
174    }
175
176    /// Find the changes from the `original` style that would result in this
177    /// style.
178    ///
179    /// This can be useful for writers like `ansi` that are stateful, finding
180    /// the minimal state that must be changed between the current output
181    /// style and the new style.
182    ///
183    /// ```rust
184    /// use stylish::{Color, Foreground, Style, StyleDiff};
185    ///
186    /// let original = Style::default();
187    /// let updated = original.with(Foreground(Color::Cyan));
188    ///
189    /// assert!(matches!(
190    ///     updated.diff_from(original),
191    ///     StyleDiff {
192    ///         foreground: Some(Color::Cyan),
193    ///         background: None,
194    ///         intensity: None,
195    ///         ..
196    ///     }
197    /// ));
198    /// ```
199    #[inline]
200    pub fn diff_from(self, original: Style) -> StyleDiff {
201        fn diff<T: PartialEq>(original: T, new: T) -> Option<T> {
202            if original == new {
203                None
204            } else {
205                Some(new)
206            }
207        }
208
209        StyleDiff {
210            foreground: diff(original.foreground, self.foreground),
211            background: diff(original.background, self.background),
212            intensity: diff(original.intensity, self.intensity),
213        }
214    }
215}
216
217impl<T: Restyle + ?Sized> Restyle for &T {
218    fn apply(&self, style: Style) -> Style {
219        (**self).apply(style)
220    }
221}
222
223#[cfg(feature = "alloc")]
224impl<T: Restyle + ?Sized> Restyle for alloc::boxed::Box<T> {
225    fn apply(&self, style: Style) -> Style {
226        (**self).apply(style)
227    }
228}
229
230impl<T: Restyle> Restyle for [T] {
231    fn apply(&self, mut style: Style) -> Style {
232        for restyle in self {
233            style = restyle.apply(style);
234        }
235        style
236    }
237}
238
239impl<T: Restyle> Restyle for Option<T> {
240    fn apply(&self, style: Style) -> Style {
241        self.as_ref().map_or(style, |s| s.apply(style))
242    }
243}
244
245impl Restyle for StyleDiff {
246    #[inline]
247    fn apply(&self, style: Style) -> Style {
248        (
249            self.foreground.map(Foreground),
250            self.background.map(Background),
251            self.intensity,
252        )
253            .apply(style)
254    }
255}
256
257impl Restyle for Style {
258    #[inline]
259    fn apply(&self, _style: Style) -> Style {
260        *self
261    }
262}
263
264impl Restyle for Foreground {
265    #[inline]
266    fn apply(&self, style: Style) -> Style {
267        let &Foreground(foreground) = self;
268        Style {
269            foreground,
270            ..style
271        }
272    }
273}
274
275impl Restyle for Background {
276    #[inline]
277    fn apply(&self, style: Style) -> Style {
278        let &Background(background) = self;
279        Style {
280            background,
281            ..style
282        }
283    }
284}
285
286impl Restyle for Intensity {
287    #[inline]
288    fn apply(&self, style: Style) -> Style {
289        Style {
290            intensity: *self,
291            ..style
292        }
293    }
294}
295
296impl Restyle for () {
297    #[inline]
298    fn apply(&self, style: Style) -> Style {
299        style
300    }
301}
302
303impl<T: Restyle, U: Restyle> Restyle for (T, U) {
304    fn apply(&self, style: Style) -> Style {
305        style.with(&self.0).with(&self.1)
306    }
307}
308
309impl<T: Restyle, U: Restyle, V: Restyle> Restyle for (T, U, V) {
310    fn apply(&self, style: Style) -> Style {
311        style.with(&self.0).with(&self.1).with(&self.2)
312    }
313}