anstyle/
style.rs

1use crate::reset::RESET;
2
3/// ANSI Text styling
4///
5/// You can print a `Style` to render the corresponding ANSI code.
6/// Using the alternate flag `#` will render the ANSI reset code, if needed.
7/// Together, this makes it convenient to render styles using inline format arguments.
8///
9/// # Examples
10///
11/// ```rust
12/// let style = anstyle::Style::new().bold();
13///
14/// let value = 42;
15/// println!("{style}{value}{style:#}");
16/// ```
17#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct Style {
19    fg: Option<crate::Color>,
20    bg: Option<crate::Color>,
21    underline: Option<crate::Color>,
22    effects: crate::Effects,
23}
24
25/// # Core
26impl Style {
27    /// No effects enabled
28    ///
29    /// # Examples
30    ///
31    /// ```rust
32    /// let style = anstyle::Style::new();
33    /// ```
34    #[inline]
35    pub const fn new() -> Self {
36        Self {
37            fg: None,
38            bg: None,
39            underline: None,
40            effects: crate::Effects::new(),
41        }
42    }
43
44    /// Set foreground color
45    ///
46    /// # Examples
47    ///
48    /// ```rust
49    /// let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into()));
50    /// ```
51    #[must_use]
52    #[inline]
53    pub const fn fg_color(mut self, fg: Option<crate::Color>) -> Self {
54        self.fg = fg;
55        self
56    }
57
58    /// Set background color
59    ///
60    /// # Examples
61    ///
62    /// ```rust
63    /// let style = anstyle::Style::new().bg_color(Some(anstyle::AnsiColor::Red.into()));
64    /// ```
65    #[must_use]
66    #[inline]
67    pub const fn bg_color(mut self, bg: Option<crate::Color>) -> Self {
68        self.bg = bg;
69        self
70    }
71
72    /// Set underline color
73    ///
74    /// # Examples
75    ///
76    /// ```rust
77    /// let style = anstyle::Style::new().underline_color(Some(anstyle::AnsiColor::Red.into()));
78    /// ```
79    #[must_use]
80    #[inline]
81    pub const fn underline_color(mut self, underline: Option<crate::Color>) -> Self {
82        self.underline = underline;
83        self
84    }
85
86    /// Set text effects
87    ///
88    /// # Examples
89    ///
90    /// ```rust
91    /// let style = anstyle::Style::new().effects(anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE);
92    /// ```
93    #[must_use]
94    #[inline]
95    pub const fn effects(mut self, effects: crate::Effects) -> Self {
96        self.effects = effects;
97        self
98    }
99
100    /// Render the ANSI code
101    ///
102    /// `Style` also implements `Display` directly, so calling this method is optional.
103    #[inline]
104    pub fn render(self) -> impl core::fmt::Display + Copy {
105        StyleDisplay(self)
106    }
107
108    fn fmt_to(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
109        use core::fmt::Display as _;
110
111        self.effects.render().fmt(f)?;
112
113        if let Some(fg) = self.fg {
114            fg.render_fg().fmt(f)?;
115        }
116
117        if let Some(bg) = self.bg {
118            bg.render_bg().fmt(f)?;
119        }
120
121        if let Some(underline) = self.underline {
122            underline.render_underline().fmt(f)?;
123        }
124
125        Ok(())
126    }
127
128    /// Write the ANSI code
129    #[inline]
130    #[cfg(feature = "std")]
131    pub fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
132        self.effects.write_to(write)?;
133
134        if let Some(fg) = self.fg {
135            fg.write_fg_to(write)?;
136        }
137
138        if let Some(bg) = self.bg {
139            bg.write_bg_to(write)?;
140        }
141
142        if let Some(underline) = self.underline {
143            underline.write_underline_to(write)?;
144        }
145
146        Ok(())
147    }
148
149    /// Renders the relevant [`Reset`][crate::Reset] code
150    ///
151    /// Unlike [`Reset::render`][crate::Reset::render], this will elide the code if there is nothing to reset.
152    #[inline]
153    pub fn render_reset(self) -> impl core::fmt::Display + Copy {
154        if self != Self::new() {
155            RESET
156        } else {
157            ""
158        }
159    }
160
161    /// Write the relevant [`Reset`][crate::Reset] code
162    ///
163    /// Unlike [`Reset::render`][crate::Reset::render], this will elide the code if there is nothing to reset.
164    #[inline]
165    #[cfg(feature = "std")]
166    pub fn write_reset_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
167        if self != Self::new() {
168            write.write_all(RESET.as_bytes())
169        } else {
170            Ok(())
171        }
172    }
173}
174
175/// # Convenience
176impl Style {
177    /// Apply `bold` effect
178    ///
179    /// # Examples
180    ///
181    /// ```rust
182    /// let style = anstyle::Style::new().bold();
183    /// ```
184    #[must_use]
185    #[inline]
186    pub const fn bold(mut self) -> Self {
187        self.effects = self.effects.insert(crate::Effects::BOLD);
188        self
189    }
190
191    /// Apply `dimmed` effect
192    ///
193    /// # Examples
194    ///
195    /// ```rust
196    /// let style = anstyle::Style::new().dimmed();
197    /// ```
198    #[must_use]
199    #[inline]
200    pub const fn dimmed(mut self) -> Self {
201        self.effects = self.effects.insert(crate::Effects::DIMMED);
202        self
203    }
204
205    /// Apply `italic` effect
206    ///
207    /// # Examples
208    ///
209    /// ```rust
210    /// let style = anstyle::Style::new().italic();
211    /// ```
212    #[must_use]
213    #[inline]
214    pub const fn italic(mut self) -> Self {
215        self.effects = self.effects.insert(crate::Effects::ITALIC);
216        self
217    }
218
219    /// Apply `underline` effect
220    ///
221    /// # Examples
222    ///
223    /// ```rust
224    /// let style = anstyle::Style::new().underline();
225    /// ```
226    #[must_use]
227    #[inline]
228    pub const fn underline(mut self) -> Self {
229        self.effects = self.effects.insert(crate::Effects::UNDERLINE);
230        self
231    }
232
233    /// Apply `blink` effect
234    ///
235    /// # Examples
236    ///
237    /// ```rust
238    /// let style = anstyle::Style::new().blink();
239    /// ```
240    #[must_use]
241    #[inline]
242    pub const fn blink(mut self) -> Self {
243        self.effects = self.effects.insert(crate::Effects::BLINK);
244        self
245    }
246
247    /// Apply `invert` effect
248    ///
249    /// # Examples
250    ///
251    /// ```rust
252    /// let style = anstyle::Style::new().invert();
253    /// ```
254    #[must_use]
255    #[inline]
256    pub const fn invert(mut self) -> Self {
257        self.effects = self.effects.insert(crate::Effects::INVERT);
258        self
259    }
260
261    /// Apply `hidden` effect
262    ///
263    /// # Examples
264    ///
265    /// ```rust
266    /// let style = anstyle::Style::new().hidden();
267    /// ```
268    #[must_use]
269    #[inline]
270    pub const fn hidden(mut self) -> Self {
271        self.effects = self.effects.insert(crate::Effects::HIDDEN);
272        self
273    }
274
275    /// Apply `strikethrough` effect
276    ///
277    /// # Examples
278    ///
279    /// ```rust
280    /// let style = anstyle::Style::new().strikethrough();
281    /// ```
282    #[must_use]
283    #[inline]
284    pub const fn strikethrough(mut self) -> Self {
285        self.effects = self.effects.insert(crate::Effects::STRIKETHROUGH);
286        self
287    }
288}
289
290/// # Reflection
291impl Style {
292    /// Get the foreground color
293    #[inline]
294    pub const fn get_fg_color(self) -> Option<crate::Color> {
295        self.fg
296    }
297
298    /// Get the background color
299    #[inline]
300    #[allow(missing_docs)]
301    pub const fn get_bg_color(self) -> Option<crate::Color> {
302        self.bg
303    }
304
305    #[inline]
306    #[allow(missing_docs)]
307    pub const fn get_underline_color(self) -> Option<crate::Color> {
308        self.underline
309    }
310
311    #[inline]
312    #[allow(missing_docs)]
313    pub const fn get_effects(self) -> crate::Effects {
314        self.effects
315    }
316
317    /// Check if no styling is enabled
318    #[inline]
319    pub const fn is_plain(self) -> bool {
320        self.fg.is_none()
321            && self.bg.is_none()
322            && self.underline.is_none()
323            && self.effects.is_plain()
324    }
325}
326
327/// # Examples
328///
329/// ```rust
330/// let style: anstyle::Style = anstyle::Effects::BOLD.into();
331/// ```
332impl From<crate::Effects> for Style {
333    #[inline]
334    fn from(effects: crate::Effects) -> Self {
335        Self::new().effects(effects)
336    }
337}
338
339/// # Examples
340///
341/// ```rust
342/// let style = anstyle::Style::new() | anstyle::Effects::BOLD.into();
343/// ```
344impl core::ops::BitOr<crate::Effects> for Style {
345    type Output = Self;
346
347    #[inline(always)]
348    fn bitor(mut self, rhs: crate::Effects) -> Self {
349        self.effects |= rhs;
350        self
351    }
352}
353
354/// # Examples
355///
356/// ```rust
357/// let mut style = anstyle::Style::new();
358/// style |= anstyle::Effects::BOLD.into();
359/// ```
360impl core::ops::BitOrAssign<crate::Effects> for Style {
361    #[inline]
362    fn bitor_assign(&mut self, other: crate::Effects) {
363        self.effects |= other;
364    }
365}
366
367/// # Examples
368///
369/// ```rust
370/// let style = anstyle::Style::new().bold().underline() - anstyle::Effects::BOLD.into();
371/// ```
372impl core::ops::Sub<crate::Effects> for Style {
373    type Output = Self;
374
375    #[inline]
376    fn sub(mut self, other: crate::Effects) -> Self {
377        self.effects -= other;
378        self
379    }
380}
381
382/// # Examples
383///
384/// ```rust
385/// let mut style = anstyle::Style::new().bold().underline();
386/// style -= anstyle::Effects::BOLD.into();
387/// ```
388impl core::ops::SubAssign<crate::Effects> for Style {
389    #[inline]
390    fn sub_assign(&mut self, other: crate::Effects) {
391        self.effects -= other;
392    }
393}
394
395/// # Examples
396///
397/// ```rust
398/// let effects = anstyle::Effects::BOLD;
399/// assert_eq!(anstyle::Style::new().effects(effects), effects);
400/// assert_ne!(anstyle::Effects::UNDERLINE | effects, effects);
401/// assert_ne!(anstyle::RgbColor(0, 0, 0).on_default() | effects, effects);
402/// ```
403impl PartialEq<crate::Effects> for Style {
404    #[inline]
405    fn eq(&self, other: &crate::Effects) -> bool {
406        let other = Self::from(*other);
407        *self == other
408    }
409}
410
411impl core::fmt::Display for Style {
412    #[inline]
413    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
414        if f.alternate() {
415            self.render_reset().fmt(f)
416        } else {
417            self.fmt_to(f)
418        }
419    }
420}
421
422#[derive(Copy, Clone, Default, Debug)]
423struct StyleDisplay(Style);
424
425impl core::fmt::Display for StyleDisplay {
426    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
427        self.0.fmt_to(f)
428    }
429}
430
431#[test]
432#[cfg(feature = "std")]
433fn print_size_of() {
434    use std::mem::size_of;
435    dbg!(size_of::<Style>());
436    dbg!(size_of::<StyleDisplay>());
437}