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}