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