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