Skip to main content

console/
utils.rs

1use alloc::borrow::Cow;
2use core::{
3    fmt::{self, Debug, Formatter},
4    sync::atomic::{AtomicBool, Ordering},
5};
6use std::env;
7
8use std::sync::OnceLock;
9
10use crate::term::{wants_emoji, Term};
11
12#[cfg(feature = "ansi-parsing")]
13use crate::ansi::AnsiCodeIterator;
14
15fn default_colors_enabled(out: &Term) -> bool {
16    (out.features().colors_supported()
17        && &env::var("CLICOLOR").unwrap_or_else(|_| "1".into()) != "0")
18        || &env::var("CLICOLOR_FORCE").unwrap_or_else(|_| "0".into()) != "0"
19}
20
21fn default_true_colors_enabled(out: &Term) -> bool {
22    out.features().true_colors_supported()
23}
24
25fn stdout_colors() -> &'static AtomicBool {
26    static ENABLED: OnceLock<AtomicBool> = OnceLock::new();
27    ENABLED.get_or_init(|| AtomicBool::new(default_colors_enabled(&Term::stdout())))
28}
29fn stdout_true_colors() -> &'static AtomicBool {
30    static ENABLED: OnceLock<AtomicBool> = OnceLock::new();
31    ENABLED.get_or_init(|| AtomicBool::new(default_true_colors_enabled(&Term::stdout())))
32}
33fn stderr_colors() -> &'static AtomicBool {
34    static ENABLED: OnceLock<AtomicBool> = OnceLock::new();
35    ENABLED.get_or_init(|| AtomicBool::new(default_colors_enabled(&Term::stderr())))
36}
37fn stderr_true_colors() -> &'static AtomicBool {
38    static ENABLED: OnceLock<AtomicBool> = OnceLock::new();
39    ENABLED.get_or_init(|| AtomicBool::new(default_true_colors_enabled(&Term::stderr())))
40}
41
42/// Returns `true` if colors should be enabled for stdout.
43///
44/// This honors the [clicolors spec](http://bixense.com/clicolors/).
45///
46/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
47/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
48/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
49#[inline]
50pub fn colors_enabled() -> bool {
51    stdout_colors().load(Ordering::Relaxed)
52}
53
54/// Returns `true` if true colors should be enabled for stdout.
55#[inline]
56pub fn true_colors_enabled() -> bool {
57    stdout_true_colors().load(Ordering::Relaxed)
58}
59
60/// Forces colorization on or off for stdout.
61///
62/// This overrides the default for the current process and changes the return value of the
63/// `colors_enabled` function.
64#[inline]
65pub fn set_colors_enabled(val: bool) {
66    stdout_colors().store(val, Ordering::Relaxed)
67}
68
69/// Forces true colorization on or off for stdout.
70///
71/// This overrides the default for the current process and changes the return value of the
72/// `true_colors_enabled` function.
73#[inline]
74pub fn set_true_colors_enabled(val: bool) {
75    stdout_true_colors().store(val, Ordering::Relaxed)
76}
77
78/// Returns `true` if colors should be enabled for stderr.
79///
80/// This honors the [clicolors spec](http://bixense.com/clicolors/).
81///
82/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
83/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
84/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
85#[inline]
86pub fn colors_enabled_stderr() -> bool {
87    stderr_colors().load(Ordering::Relaxed)
88}
89
90/// Returns `true` if true colors should be enabled for stderr.
91#[inline]
92pub fn true_colors_enabled_stderr() -> bool {
93    stderr_true_colors().load(Ordering::Relaxed)
94}
95
96/// Forces colorization on or off for stderr.
97///
98/// This overrides the default for the current process and changes the return value of the
99/// `colors_enabled_stderr` function.
100#[inline]
101pub fn set_colors_enabled_stderr(val: bool) {
102    stderr_colors().store(val, Ordering::Relaxed)
103}
104
105/// Forces true colorization on or off for stderr.
106///
107/// This overrides the default for the current process and changes the return value of the
108/// `true_colors_enabled_stderr` function.
109#[inline]
110pub fn set_true_colors_enabled_stderr(val: bool) {
111    stderr_true_colors().store(val, Ordering::Relaxed)
112}
113
114/// Measure the width of a string in terminal characters.
115pub fn measure_text_width(s: &str) -> usize {
116    #[cfg(feature = "ansi-parsing")]
117    {
118        AnsiCodeIterator::new(s)
119            .filter_map(|(s, is_ansi)| match is_ansi {
120                false => Some(str_width(s)),
121                true => None,
122            })
123            .sum()
124    }
125    #[cfg(not(feature = "ansi-parsing"))]
126    {
127        str_width(s)
128    }
129}
130
131/// A terminal color.
132#[derive(Copy, Clone, Debug, PartialEq, Eq)]
133pub enum Color {
134    Black,
135    Red,
136    Green,
137    Yellow,
138    Blue,
139    Magenta,
140    Cyan,
141    White,
142    Color256(u8),
143    TrueColor(u8, u8, u8),
144}
145
146impl Color {
147    #[inline]
148    fn ansi_num(self) -> usize {
149        match self {
150            Color::Black => 0,
151            Color::Red => 1,
152            Color::Green => 2,
153            Color::Yellow => 3,
154            Color::Blue => 4,
155            Color::Magenta => 5,
156            Color::Cyan => 6,
157            Color::White => 7,
158            Color::Color256(x) => x as usize,
159            Color::TrueColor(_, _, _) => panic!("RGB colors must be handled separately"),
160        }
161    }
162
163    #[inline]
164    fn is_color256(self) -> bool {
165        #[allow(clippy::match_like_matches_macro)]
166        match self {
167            Color::Color256(_) => true,
168            _ => false,
169        }
170    }
171}
172
173/// A terminal style attribute.
174#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
175#[repr(u16)]
176pub enum Attribute {
177    // This mapping is important, it exactly matches ansi_num = (x as u16 + 1)
178    // See `ATTRIBUTES_LOOKUP` as well
179    Bold = 0,
180    Dim = 1,
181    Italic = 2,
182    Underlined = 3,
183    Blink = 4,
184    BlinkFast = 5,
185    Reverse = 6,
186    Hidden = 7,
187    StrikeThrough = 8,
188}
189
190impl Attribute {
191    const MAP: [Attribute; 9] = [
192        Attribute::Bold,
193        Attribute::Dim,
194        Attribute::Italic,
195        Attribute::Underlined,
196        Attribute::Blink,
197        Attribute::BlinkFast,
198        Attribute::Reverse,
199        Attribute::Hidden,
200        Attribute::StrikeThrough,
201    ];
202}
203
204#[derive(Clone, Copy, PartialEq, Eq)]
205struct Attributes(u16);
206
207impl Attributes {
208    #[inline]
209    const fn new() -> Self {
210        Self(0)
211    }
212
213    #[inline]
214    #[must_use]
215    const fn insert(mut self, attr: Attribute) -> Self {
216        let bit = attr as u16;
217        self.0 |= 1 << bit;
218        self
219    }
220
221    #[inline]
222    const fn bits(self) -> BitsIter {
223        BitsIter(self.0)
224    }
225
226    #[inline]
227    fn attrs(self) -> impl Iterator<Item = Attribute> {
228        self.bits().map(|bit| Attribute::MAP[bit as usize])
229    }
230
231    #[inline]
232    fn is_empty(self) -> bool {
233        self.0 == 0
234    }
235}
236
237impl fmt::Display for Attributes {
238    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
239        for ansi in self.bits().map(|bit| bit + 1) {
240            write!(f, "\x1b[{ansi}m")?;
241        }
242        Ok(())
243    }
244}
245
246struct BitsIter(u16);
247
248impl Iterator for BitsIter {
249    type Item = u16;
250
251    fn next(&mut self) -> Option<Self::Item> {
252        if self.0 == 0 {
253            return None;
254        }
255        let bit = self.0.trailing_zeros();
256        self.0 ^= (1 << bit) as u16;
257        Some(bit as u16)
258    }
259}
260
261impl Debug for Attributes {
262    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
263        f.debug_set().entries(self.attrs()).finish()
264    }
265}
266
267/// Defines the alignment for padding operations.
268#[derive(Copy, Clone, Debug, PartialEq, Eq)]
269pub enum Alignment {
270    Left,
271    Center,
272    Right,
273}
274
275/// A stored style that can be applied.
276#[derive(Clone, Debug, PartialEq, Eq)]
277pub struct Style {
278    fg: Option<Color>,
279    bg: Option<Color>,
280    fg_bright: bool,
281    bg_bright: bool,
282    attrs: Attributes,
283    force: Option<bool>,
284    for_stderr: bool,
285}
286
287impl Default for Style {
288    fn default() -> Self {
289        Self::new()
290    }
291}
292
293impl Style {
294    /// Returns an empty default style.
295    pub const fn new() -> Self {
296        Self {
297            fg: None,
298            bg: None,
299            fg_bright: false,
300            bg_bright: false,
301            attrs: Attributes::new(),
302            force: None,
303            for_stderr: false,
304        }
305    }
306
307    /// Creates a style from a dotted string.
308    ///
309    /// Effectively the string is split at each dot and then the
310    /// terms in between are applied.  For instance `red.on_blue` will
311    /// create a string that is red on blue background. `9.on_12` is
312    /// the same, but using 256 color numbers. Unknown terms are
313    /// ignored.
314    pub fn from_dotted_str(s: &str) -> Self {
315        let mut rv = Self::new();
316        for part in s.split('.') {
317            rv = match part {
318                "black" => rv.black(),
319                "red" => rv.red(),
320                "green" => rv.green(),
321                "yellow" => rv.yellow(),
322                "blue" => rv.blue(),
323                "magenta" => rv.magenta(),
324                "cyan" => rv.cyan(),
325                "white" => rv.white(),
326                "bright" => rv.bright(),
327                "on_black" => rv.on_black(),
328                "on_red" => rv.on_red(),
329                "on_green" => rv.on_green(),
330                "on_yellow" => rv.on_yellow(),
331                "on_blue" => rv.on_blue(),
332                "on_magenta" => rv.on_magenta(),
333                "on_cyan" => rv.on_cyan(),
334                "on_white" => rv.on_white(),
335                "on_bright" => rv.on_bright(),
336                "bold" => rv.bold(),
337                "dim" => rv.dim(),
338                "underlined" => rv.underlined(),
339                "blink" => rv.blink(),
340                "blink_fast" => rv.blink_fast(),
341                "reverse" => rv.reverse(),
342                "hidden" => rv.hidden(),
343                "strikethrough" => rv.strikethrough(),
344                on_true_color if on_true_color.starts_with("on_#") && on_true_color.len() == 10 => {
345                    if let (Ok(r), Ok(g), Ok(b)) = (
346                        u8::from_str_radix(&on_true_color[4..6], 16),
347                        u8::from_str_radix(&on_true_color[6..8], 16),
348                        u8::from_str_radix(&on_true_color[8..10], 16),
349                    ) {
350                        rv.on_true_color(r, g, b)
351                    } else {
352                        continue;
353                    }
354                }
355                true_color if true_color.starts_with('#') && true_color.len() == 7 => {
356                    if let (Ok(r), Ok(g), Ok(b)) = (
357                        u8::from_str_radix(&true_color[1..3], 16),
358                        u8::from_str_radix(&true_color[3..5], 16),
359                        u8::from_str_radix(&true_color[5..7], 16),
360                    ) {
361                        rv.true_color(r, g, b)
362                    } else {
363                        continue;
364                    }
365                }
366                on_c if on_c.starts_with("on_") => {
367                    if let Ok(n) = on_c[3..].parse::<u8>() {
368                        rv.on_color256(n)
369                    } else {
370                        continue;
371                    }
372                }
373                c => {
374                    if let Ok(n) = c.parse::<u8>() {
375                        rv.color256(n)
376                    } else {
377                        continue;
378                    }
379                }
380            };
381        }
382        rv
383    }
384
385    /// Apply the style to something that can be displayed.
386    pub fn apply_to<D>(&self, val: D) -> StyledObject<D> {
387        StyledObject {
388            style: self.clone(),
389            val,
390        }
391    }
392
393    /// Forces styling on or off.
394    ///
395    /// This overrides the automatic detection.
396    #[inline]
397    pub const fn force_styling(mut self, value: bool) -> Self {
398        self.force = Some(value);
399        self
400    }
401
402    /// Specifies that style is applying to something being written on stderr.
403    #[inline]
404    pub const fn for_stderr(mut self) -> Self {
405        self.for_stderr = true;
406        self
407    }
408
409    /// Specifies that style is applying to something being written on stdout.
410    ///
411    /// This is the default behaviour.
412    #[inline]
413    pub const fn for_stdout(mut self) -> Self {
414        self.for_stderr = false;
415        self
416    }
417
418    /// Sets a foreground color.
419    #[inline]
420    pub const fn fg(mut self, color: Color) -> Self {
421        self.fg = Some(color);
422        self
423    }
424
425    /// Sets a background color.
426    #[inline]
427    pub const fn bg(mut self, color: Color) -> Self {
428        self.bg = Some(color);
429        self
430    }
431
432    /// Adds a attr.
433    #[inline]
434    pub const fn attr(mut self, attr: Attribute) -> Self {
435        self.attrs = self.attrs.insert(attr);
436        self
437    }
438
439    #[inline]
440    pub const fn black(self) -> Self {
441        self.fg(Color::Black)
442    }
443    #[inline]
444    pub const fn red(self) -> Self {
445        self.fg(Color::Red)
446    }
447    #[inline]
448    pub const fn green(self) -> Self {
449        self.fg(Color::Green)
450    }
451    #[inline]
452    pub const fn yellow(self) -> Self {
453        self.fg(Color::Yellow)
454    }
455    #[inline]
456    pub const fn blue(self) -> Self {
457        self.fg(Color::Blue)
458    }
459    #[inline]
460    pub const fn magenta(self) -> Self {
461        self.fg(Color::Magenta)
462    }
463    #[inline]
464    pub const fn cyan(self) -> Self {
465        self.fg(Color::Cyan)
466    }
467    #[inline]
468    pub const fn white(self) -> Self {
469        self.fg(Color::White)
470    }
471    #[inline]
472    pub const fn color256(self, color: u8) -> Self {
473        self.fg(Color::Color256(color))
474    }
475    #[inline]
476    pub const fn true_color(self, r: u8, g: u8, b: u8) -> Self {
477        self.fg(Color::TrueColor(r, g, b))
478    }
479
480    #[inline]
481    pub const fn bright(mut self) -> Self {
482        self.fg_bright = true;
483        self
484    }
485
486    #[inline]
487    pub const fn on_black(self) -> Self {
488        self.bg(Color::Black)
489    }
490    #[inline]
491    pub const fn on_red(self) -> Self {
492        self.bg(Color::Red)
493    }
494    #[inline]
495    pub const fn on_green(self) -> Self {
496        self.bg(Color::Green)
497    }
498    #[inline]
499    pub const fn on_yellow(self) -> Self {
500        self.bg(Color::Yellow)
501    }
502    #[inline]
503    pub const fn on_blue(self) -> Self {
504        self.bg(Color::Blue)
505    }
506    #[inline]
507    pub const fn on_magenta(self) -> Self {
508        self.bg(Color::Magenta)
509    }
510    #[inline]
511    pub const fn on_cyan(self) -> Self {
512        self.bg(Color::Cyan)
513    }
514    #[inline]
515    pub const fn on_white(self) -> Self {
516        self.bg(Color::White)
517    }
518    #[inline]
519    pub const fn on_color256(self, color: u8) -> Self {
520        self.bg(Color::Color256(color))
521    }
522    #[inline]
523    pub const fn on_true_color(self, r: u8, g: u8, b: u8) -> Self {
524        self.bg(Color::TrueColor(r, g, b))
525    }
526
527    #[inline]
528    pub const fn on_bright(mut self) -> Self {
529        self.bg_bright = true;
530        self
531    }
532
533    #[inline]
534    pub const fn bold(self) -> Self {
535        self.attr(Attribute::Bold)
536    }
537    #[inline]
538    pub const fn dim(self) -> Self {
539        self.attr(Attribute::Dim)
540    }
541    #[inline]
542    pub const fn italic(self) -> Self {
543        self.attr(Attribute::Italic)
544    }
545    #[inline]
546    pub const fn underlined(self) -> Self {
547        self.attr(Attribute::Underlined)
548    }
549    #[inline]
550    pub const fn blink(self) -> Self {
551        self.attr(Attribute::Blink)
552    }
553    #[inline]
554    pub const fn blink_fast(self) -> Self {
555        self.attr(Attribute::BlinkFast)
556    }
557    #[inline]
558    pub const fn reverse(self) -> Self {
559        self.attr(Attribute::Reverse)
560    }
561    #[inline]
562    pub const fn hidden(self) -> Self {
563        self.attr(Attribute::Hidden)
564    }
565    #[inline]
566    pub const fn strikethrough(self) -> Self {
567        self.attr(Attribute::StrikeThrough)
568    }
569}
570
571/// Wraps an object for formatting for styling.
572///
573/// Example:
574///
575/// ```rust,no_run
576/// # use console::style;
577/// format!("Hello {}", style("World").cyan());
578/// ```
579///
580/// This is a shortcut for making a new style and applying it
581/// to a value:
582///
583/// ```rust,no_run
584/// # use console::Style;
585/// format!("Hello {}", Style::new().cyan().apply_to("World"));
586/// ```
587pub fn style<D>(val: D) -> StyledObject<D> {
588    Style::new().apply_to(val)
589}
590
591/// A formatting wrapper that can be styled for a terminal.
592#[derive(Clone)]
593pub struct StyledObject<D> {
594    style: Style,
595    val: D,
596}
597
598impl<D> StyledObject<D> {
599    /// Forces styling on or off.
600    ///
601    /// This overrides the automatic detection.
602    #[inline]
603    pub fn force_styling(mut self, value: bool) -> StyledObject<D> {
604        self.style = self.style.force_styling(value);
605        self
606    }
607
608    /// Specifies that style is applying to something being written on stderr
609    #[inline]
610    pub fn for_stderr(mut self) -> StyledObject<D> {
611        self.style = self.style.for_stderr();
612        self
613    }
614
615    /// Specifies that style is applying to something being written on stdout
616    ///
617    /// This is the default
618    #[inline]
619    pub const fn for_stdout(mut self) -> StyledObject<D> {
620        self.style = self.style.for_stdout();
621        self
622    }
623
624    /// Sets a foreground color.
625    #[inline]
626    pub const fn fg(mut self, color: Color) -> StyledObject<D> {
627        self.style = self.style.fg(color);
628        self
629    }
630
631    /// Sets a background color.
632    #[inline]
633    pub const fn bg(mut self, color: Color) -> StyledObject<D> {
634        self.style = self.style.bg(color);
635        self
636    }
637
638    /// Adds a attr.
639    #[inline]
640    pub const fn attr(mut self, attr: Attribute) -> StyledObject<D> {
641        self.style = self.style.attr(attr);
642        self
643    }
644
645    #[inline]
646    pub const fn black(self) -> StyledObject<D> {
647        self.fg(Color::Black)
648    }
649    #[inline]
650    pub const fn red(self) -> StyledObject<D> {
651        self.fg(Color::Red)
652    }
653    #[inline]
654    pub const fn green(self) -> StyledObject<D> {
655        self.fg(Color::Green)
656    }
657    #[inline]
658    pub const fn yellow(self) -> StyledObject<D> {
659        self.fg(Color::Yellow)
660    }
661    #[inline]
662    pub const fn blue(self) -> StyledObject<D> {
663        self.fg(Color::Blue)
664    }
665    #[inline]
666    pub const fn magenta(self) -> StyledObject<D> {
667        self.fg(Color::Magenta)
668    }
669    #[inline]
670    pub const fn cyan(self) -> StyledObject<D> {
671        self.fg(Color::Cyan)
672    }
673    #[inline]
674    pub const fn white(self) -> StyledObject<D> {
675        self.fg(Color::White)
676    }
677    #[inline]
678    pub const fn color256(self, color: u8) -> StyledObject<D> {
679        self.fg(Color::Color256(color))
680    }
681    #[inline]
682    pub const fn true_color(self, r: u8, g: u8, b: u8) -> StyledObject<D> {
683        self.fg(Color::TrueColor(r, g, b))
684    }
685
686    #[inline]
687    pub const fn bright(mut self) -> StyledObject<D> {
688        self.style = self.style.bright();
689        self
690    }
691
692    #[inline]
693    pub const fn on_black(self) -> StyledObject<D> {
694        self.bg(Color::Black)
695    }
696    #[inline]
697    pub const fn on_red(self) -> StyledObject<D> {
698        self.bg(Color::Red)
699    }
700    #[inline]
701    pub const fn on_green(self) -> StyledObject<D> {
702        self.bg(Color::Green)
703    }
704    #[inline]
705    pub const fn on_yellow(self) -> StyledObject<D> {
706        self.bg(Color::Yellow)
707    }
708    #[inline]
709    pub const fn on_blue(self) -> StyledObject<D> {
710        self.bg(Color::Blue)
711    }
712    #[inline]
713    pub const fn on_magenta(self) -> StyledObject<D> {
714        self.bg(Color::Magenta)
715    }
716    #[inline]
717    pub const fn on_cyan(self) -> StyledObject<D> {
718        self.bg(Color::Cyan)
719    }
720    #[inline]
721    pub const fn on_white(self) -> StyledObject<D> {
722        self.bg(Color::White)
723    }
724    #[inline]
725    pub const fn on_color256(self, color: u8) -> StyledObject<D> {
726        self.bg(Color::Color256(color))
727    }
728    #[inline]
729    pub const fn on_true_color(self, r: u8, g: u8, b: u8) -> StyledObject<D> {
730        self.bg(Color::TrueColor(r, g, b))
731    }
732
733    #[inline]
734    pub const fn on_bright(mut self) -> StyledObject<D> {
735        self.style = self.style.on_bright();
736        self
737    }
738
739    #[inline]
740    pub const fn bold(self) -> StyledObject<D> {
741        self.attr(Attribute::Bold)
742    }
743    #[inline]
744    pub const fn dim(self) -> StyledObject<D> {
745        self.attr(Attribute::Dim)
746    }
747    #[inline]
748    pub const fn italic(self) -> StyledObject<D> {
749        self.attr(Attribute::Italic)
750    }
751    #[inline]
752    pub const fn underlined(self) -> StyledObject<D> {
753        self.attr(Attribute::Underlined)
754    }
755    #[inline]
756    pub const fn blink(self) -> StyledObject<D> {
757        self.attr(Attribute::Blink)
758    }
759    #[inline]
760    pub const fn blink_fast(self) -> StyledObject<D> {
761        self.attr(Attribute::BlinkFast)
762    }
763    #[inline]
764    pub const fn reverse(self) -> StyledObject<D> {
765        self.attr(Attribute::Reverse)
766    }
767    #[inline]
768    pub const fn hidden(self) -> StyledObject<D> {
769        self.attr(Attribute::Hidden)
770    }
771    #[inline]
772    pub const fn strikethrough(self) -> StyledObject<D> {
773        self.attr(Attribute::StrikeThrough)
774    }
775}
776
777macro_rules! impl_fmt {
778    ($name:ident) => {
779        impl<D: fmt::$name> fmt::$name for StyledObject<D> {
780            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
781                let mut reset = false;
782                if self
783                    .style
784                    .force
785                    .unwrap_or_else(|| match self.style.for_stderr {
786                        true => colors_enabled_stderr(),
787                        false => colors_enabled(),
788                    })
789                {
790                    if let Some(fg) = self.style.fg {
791                        if let Color::TrueColor(r, g, b) = fg {
792                            write!(f, "\x1b[38;2;{};{};{}m", r, g, b)?;
793                        } else if fg.is_color256() {
794                            write!(f, "\x1b[38;5;{}m", fg.ansi_num())?;
795                        } else if self.style.fg_bright {
796                            write!(f, "\x1b[38;5;{}m", fg.ansi_num() + 8)?;
797                        } else {
798                            write!(f, "\x1b[{}m", fg.ansi_num() + 30)?;
799                        }
800                        reset = true;
801                    }
802                    if let Some(bg) = self.style.bg {
803                        if let Color::TrueColor(r, g, b) = bg {
804                            write!(f, "\x1b[48;2;{};{};{}m", r, g, b)?;
805                        } else if bg.is_color256() {
806                            write!(f, "\x1b[48;5;{}m", bg.ansi_num())?;
807                        } else if self.style.bg_bright {
808                            write!(f, "\x1b[48;5;{}m", bg.ansi_num() + 8)?;
809                        } else {
810                            write!(f, "\x1b[{}m", bg.ansi_num() + 40)?;
811                        }
812                        reset = true;
813                    }
814                    if !self.style.attrs.is_empty() {
815                        write!(f, "{}", self.style.attrs)?;
816                        reset = true;
817                    }
818                }
819                fmt::$name::fmt(&self.val, f)?;
820                if reset {
821                    write!(f, "\x1b[0m")?;
822                }
823                Ok(())
824            }
825        }
826    };
827}
828
829impl_fmt!(Binary);
830impl_fmt!(Debug);
831impl_fmt!(Display);
832impl_fmt!(LowerExp);
833impl_fmt!(LowerHex);
834impl_fmt!(Octal);
835impl_fmt!(Pointer);
836impl_fmt!(UpperExp);
837impl_fmt!(UpperHex);
838
839/// "Intelligent" emoji formatter.
840///
841/// This struct intelligently wraps an emoji so that it is rendered
842/// only on systems that want emojis and renders a fallback on others.
843///
844/// Example:
845///
846/// ```rust
847/// use console::Emoji;
848/// println!("[3/4] {}Downloading ...", Emoji("🚚 ", ""));
849/// println!("[4/4] {} Done!", Emoji("✨", ":-)"));
850/// ```
851#[derive(Copy, Clone)]
852pub struct Emoji<'a, 'b>(pub &'a str, pub &'b str);
853
854impl<'a, 'b> Emoji<'a, 'b> {
855    pub fn new(emoji: &'a str, fallback: &'b str) -> Emoji<'a, 'b> {
856        Emoji(emoji, fallback)
857    }
858}
859
860impl fmt::Display for Emoji<'_, '_> {
861    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
862        if wants_emoji() {
863            write!(f, "{}", self.0)
864        } else {
865            write!(f, "{}", self.1)
866        }
867    }
868}
869
870fn str_width(s: &str) -> usize {
871    #[cfg(feature = "unicode-width")]
872    {
873        use unicode_width::UnicodeWidthStr;
874        s.width()
875    }
876    #[cfg(not(feature = "unicode-width"))]
877    {
878        s.chars().count()
879    }
880}
881
882#[cfg(feature = "ansi-parsing")]
883pub(crate) fn char_width(c: char) -> usize {
884    #[cfg(feature = "unicode-width")]
885    {
886        use unicode_width::UnicodeWidthChar;
887        c.width().unwrap_or(0)
888    }
889    #[cfg(not(feature = "unicode-width"))]
890    {
891        let _c = c;
892        1
893    }
894}
895
896#[cfg(not(feature = "ansi-parsing"))]
897pub(crate) fn char_width(_c: char) -> usize {
898    1
899}
900
901/// Truncates a string to a certain number of characters.
902///
903/// This ensures that escape codes are not screwed up in the process.
904/// If the maximum length is hit the string will be truncated but
905/// escapes code will still be honored.  If truncation takes place
906/// the tail string will be appended.
907pub fn truncate_str<'a>(s: &'a str, width: usize, tail: &str) -> Cow<'a, str> {
908    if measure_text_width(s) <= width {
909        return Cow::Borrowed(s);
910    }
911
912    #[cfg(feature = "ansi-parsing")]
913    {
914        use core::cmp::Ordering;
915        let mut iter = AnsiCodeIterator::new(s);
916        let mut length = 0;
917        let mut rv = None;
918
919        while let Some(item) = iter.next() {
920            match item {
921                (s, false) => {
922                    if rv.is_none() {
923                        if str_width(s) + length > width.saturating_sub(str_width(tail)) {
924                            let ts = iter.current_slice();
925
926                            let mut s_byte = 0;
927                            let mut s_width = 0;
928                            let rest_width =
929                                width.saturating_sub(str_width(tail)).saturating_sub(length);
930                            for c in s.chars() {
931                                s_byte += c.len_utf8();
932                                s_width += char_width(c);
933                                match s_width.cmp(&rest_width) {
934                                    Ordering::Equal => break,
935                                    Ordering::Greater => {
936                                        s_byte -= c.len_utf8();
937                                        break;
938                                    }
939                                    Ordering::Less => continue,
940                                }
941                            }
942
943                            let idx = ts.len() - s.len() + s_byte;
944                            let mut buf = ts[..idx].to_string();
945                            buf.push_str(tail);
946                            rv = Some(buf);
947                        }
948                        length += str_width(s);
949                    }
950                }
951                (s, true) => {
952                    if let Some(ref mut rv) = rv {
953                        rv.push_str(s);
954                    }
955                }
956            }
957        }
958
959        if let Some(buf) = rv {
960            Cow::Owned(buf)
961        } else {
962            Cow::Borrowed(s)
963        }
964    }
965
966    #[cfg(not(feature = "ansi-parsing"))]
967    {
968        Cow::Owned(format!(
969            "{}{}",
970            &s[..width.saturating_sub(tail.len())],
971            tail
972        ))
973    }
974}
975
976/// Pads a string to fill a certain number of characters.
977///
978/// This will honor ansi codes correctly and allows you to align a string
979/// on the left, right or centered.  Additionally truncation can be enabled
980/// by setting `truncate` to a string that should be used as a truncation
981/// marker.
982pub fn pad_str<'a>(
983    s: &'a str,
984    width: usize,
985    align: Alignment,
986    truncate: Option<&str>,
987) -> Cow<'a, str> {
988    pad_str_with(s, width, align, truncate, ' ')
989}
990/// Pads a string with specific padding to fill a certain number of characters.
991///
992/// This will honor ansi codes correctly and allows you to align a string
993/// on the left, right or centered.  Additionally truncation can be enabled
994/// by setting `truncate` to a string that should be used as a truncation
995/// marker.
996pub fn pad_str_with<'a>(
997    s: &'a str,
998    width: usize,
999    align: Alignment,
1000    truncate: Option<&str>,
1001    pad: char,
1002) -> Cow<'a, str> {
1003    let cols = measure_text_width(s);
1004
1005    if cols >= width {
1006        return match truncate {
1007            None => Cow::Borrowed(s),
1008            Some(tail) => truncate_str(s, width, tail),
1009        };
1010    }
1011
1012    let diff = width - cols;
1013
1014    let (left_pad, right_pad) = match align {
1015        Alignment::Left => (0, diff),
1016        Alignment::Right => (diff, 0),
1017        Alignment::Center => (diff / 2, diff - diff / 2),
1018    };
1019
1020    let mut rv = String::new();
1021    for _ in 0..left_pad {
1022        rv.push(pad);
1023    }
1024    rv.push_str(s);
1025    for _ in 0..right_pad {
1026        rv.push(pad);
1027    }
1028    Cow::Owned(rv)
1029}
1030
1031#[test]
1032fn test_text_width() {
1033    let s = style("foo")
1034        .red()
1035        .on_black()
1036        .bold()
1037        .force_styling(true)
1038        .to_string();
1039
1040    assert_eq!(
1041        measure_text_width(&s),
1042        if cfg!(feature = "ansi-parsing") {
1043            3
1044        } else {
1045            21
1046        }
1047    );
1048
1049    let s = style("🐶 <3").red().force_styling(true).to_string();
1050
1051    assert_eq!(
1052        measure_text_width(&s),
1053        match (
1054            cfg!(feature = "ansi-parsing"),
1055            cfg!(feature = "unicode-width")
1056        ) {
1057            (true, true) => 5,    // "🐶 <3"
1058            (true, false) => 4,   // "🐶 <3", no unicode-aware width
1059            (false, true) => 14,  // full string
1060            (false, false) => 13, // full string, no unicode-aware width
1061        }
1062    );
1063}
1064
1065#[test]
1066#[cfg(all(feature = "unicode-width", feature = "ansi-parsing"))]
1067fn test_truncate_str() {
1068    let s = format!("foo {}", style("bar").red().force_styling(true));
1069    assert_eq!(
1070        &truncate_str(&s, 5, ""),
1071        &format!("foo {}", style("b").red().force_styling(true))
1072    );
1073    let s = format!("foo {}", style("bar").red().force_styling(true));
1074    assert_eq!(
1075        &truncate_str(&s, 5, "!"),
1076        &format!("foo {}", style("!").red().force_styling(true))
1077    );
1078    let s = format!("foo {} baz", style("bar").red().force_styling(true));
1079    assert_eq!(
1080        &truncate_str(&s, 10, "..."),
1081        &format!("foo {}...", style("bar").red().force_styling(true))
1082    );
1083    let s = format!("foo {}", style("バー").red().force_styling(true));
1084    assert_eq!(
1085        &truncate_str(&s, 5, ""),
1086        &format!("foo {}", style("").red().force_styling(true))
1087    );
1088    let s = format!("foo {}", style("バー").red().force_styling(true));
1089    assert_eq!(
1090        &truncate_str(&s, 6, ""),
1091        &format!("foo {}", style("バ").red().force_styling(true))
1092    );
1093    let s = format!("foo {}", style("バー").red().force_styling(true));
1094    assert_eq!(
1095        &truncate_str(&s, 2, "!!!"),
1096        &format!("!!!{}", style("").red().force_styling(true))
1097    );
1098}
1099
1100#[test]
1101fn test_truncate_str_no_ansi() {
1102    assert_eq!(&truncate_str("foo bar", 7, "!"), "foo bar");
1103    assert_eq!(&truncate_str("foo bar", 5, ""), "foo b");
1104    assert_eq!(&truncate_str("foo bar", 5, "!"), "foo !");
1105    assert_eq!(&truncate_str("foo bar baz", 10, "..."), "foo bar...");
1106    assert_eq!(&truncate_str("foo bar", 0, ""), "");
1107    assert_eq!(&truncate_str("foo bar", 0, "!"), "!");
1108    assert_eq!(&truncate_str("foo bar", 2, "!!!"), "!!!");
1109    assert_eq!(&truncate_str("ab", 2, "!!!"), "ab");
1110}
1111
1112#[test]
1113fn test_pad_str() {
1114    assert_eq!(pad_str("foo", 7, Alignment::Center, None), "  foo  ");
1115    assert_eq!(pad_str("foo", 7, Alignment::Left, None), "foo    ");
1116    assert_eq!(pad_str("foo", 7, Alignment::Right, None), "    foo");
1117    assert_eq!(pad_str("foo", 3, Alignment::Left, None), "foo");
1118    assert_eq!(pad_str("foobar", 3, Alignment::Left, None), "foobar");
1119    assert_eq!(pad_str("foobar", 3, Alignment::Left, Some("")), "foo");
1120    assert_eq!(
1121        pad_str("foobarbaz", 6, Alignment::Left, Some("...")),
1122        "foo..."
1123    );
1124}
1125
1126#[test]
1127fn test_pad_str_with() {
1128    assert_eq!(
1129        pad_str_with("foo", 7, Alignment::Center, None, '#'),
1130        "##foo##"
1131    );
1132    assert_eq!(
1133        pad_str_with("foo", 7, Alignment::Left, None, '#'),
1134        "foo####"
1135    );
1136    assert_eq!(
1137        pad_str_with("foo", 7, Alignment::Right, None, '#'),
1138        "####foo"
1139    );
1140    assert_eq!(pad_str_with("foo", 3, Alignment::Left, None, '#'), "foo");
1141    assert_eq!(
1142        pad_str_with("foobar", 3, Alignment::Left, None, '#'),
1143        "foobar"
1144    );
1145    assert_eq!(
1146        pad_str_with("foobar", 3, Alignment::Left, Some(""), '#'),
1147        "foo"
1148    );
1149    assert_eq!(
1150        pad_str_with("foobarbaz", 6, Alignment::Left, Some("..."), '#'),
1151        "foo..."
1152    );
1153}
1154
1155#[test]
1156fn test_attributes_single() {
1157    for attr in Attribute::MAP {
1158        let attrs = Attributes::new().insert(attr);
1159        assert_eq!(attrs.bits().collect::<Vec<_>>(), [attr as u16]);
1160        assert_eq!(attrs.attrs().collect::<Vec<_>>(), [attr]);
1161        assert_eq!(format!("{attrs:?}"), format!("{{{:?}}}", attr));
1162    }
1163}
1164
1165#[test]
1166fn test_attributes_many() {
1167    let tests: [&[Attribute]; 3] = [
1168        &[
1169            Attribute::Bold,
1170            Attribute::Underlined,
1171            Attribute::BlinkFast,
1172            Attribute::Hidden,
1173        ],
1174        &[
1175            Attribute::Dim,
1176            Attribute::Italic,
1177            Attribute::Blink,
1178            Attribute::Reverse,
1179            Attribute::StrikeThrough,
1180        ],
1181        &Attribute::MAP,
1182    ];
1183    for test_attrs in tests {
1184        let mut attrs = Attributes::new();
1185        for attr in test_attrs {
1186            attrs = attrs.insert(*attr);
1187        }
1188        assert_eq!(
1189            attrs.bits().collect::<Vec<_>>(),
1190            test_attrs
1191                .iter()
1192                .map(|attr| *attr as u16)
1193                .collect::<Vec<_>>()
1194        );
1195        assert_eq!(&attrs.attrs().collect::<Vec<_>>(), test_attrs);
1196    }
1197}