1
use std::borrow::Cow;
2
use std::collections::BTreeSet;
3
use std::env;
4
use std::fmt;
5
use std::sync::atomic::{AtomicBool, Ordering};
6

            
7
use lazy_static::lazy_static;
8

            
9
use crate::term::{wants_emoji, Term};
10

            
11
#[cfg(feature = "ansi-parsing")]
12
use crate::ansi::{strip_ansi_codes, AnsiCodeIterator};
13

            
14
#[cfg(not(feature = "ansi-parsing"))]
15
fn strip_ansi_codes(s: &str) -> &str {
16
    s
17
}
18

            
19
2
fn default_colors_enabled(out: &Term) -> bool {
20
2
    (out.features().colors_supported()
21
2
        && &env::var("CLICOLOR").unwrap_or_else(|_| "1".into()) != "0")
22
        || &env::var("CLICOLOR_FORCE").unwrap_or_else(|_| "0".into()) != "0"
23
2
}
24

            
25
lazy_static! {
26
    static ref STDOUT_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stdout()));
27
    static ref STDERR_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stderr()));
28
}
29

            
30
/// Returns `true` if colors should be enabled for stdout.
31
///
32
/// This honors the [clicolors spec](http://bixense.com/clicolors/).
33
///
34
/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
35
/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
36
/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
37
#[inline]
38
35184
pub fn colors_enabled() -> bool {
39
35184
    STDOUT_COLORS.load(Ordering::Relaxed)
40
35184
}
41

            
42
/// Forces colorization on or off for stdout.
43
///
44
/// This overrides the default for the current process and changes the return value of the
45
/// `colors_enabled` function.
46
#[inline]
47
pub fn set_colors_enabled(val: bool) {
48
    STDOUT_COLORS.store(val, Ordering::Relaxed)
49
}
50

            
51
/// Returns `true` if colors should be enabled for stderr.
52
///
53
/// This honors the [clicolors spec](http://bixense.com/clicolors/).
54
///
55
/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
56
/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
57
/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
58
#[inline]
59
pub fn colors_enabled_stderr() -> bool {
60
    STDERR_COLORS.load(Ordering::Relaxed)
61
}
62

            
63
/// Forces colorization on or off for stderr.
64
///
65
/// This overrides the default for the current process and changes the return value of the
66
/// `colors_enabled` function.
67
#[inline]
68
pub fn set_colors_enabled_stderr(val: bool) {
69
    STDERR_COLORS.store(val, Ordering::Relaxed)
70
}
71

            
72
/// Measure the width of a string in terminal characters.
73
70368
pub fn measure_text_width(s: &str) -> usize {
74
70368
    str_width(&strip_ansi_codes(s))
75
70368
}
76

            
77
/// A terminal color.
78
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
79
pub enum Color {
80
    Black,
81
    Red,
82
    Green,
83
    Yellow,
84
    Blue,
85
    Magenta,
86
    Cyan,
87
    White,
88
    Color256(u8),
89
}
90

            
91
impl Color {
92
    #[inline]
93
35184
    fn ansi_num(self) -> usize {
94
35184
        match self {
95
            Color::Black => 0,
96
            Color::Red => 1,
97
            Color::Green => 2,
98
            Color::Yellow => 3,
99
17592
            Color::Blue => 4,
100
            Color::Magenta => 5,
101
17592
            Color::Cyan => 6,
102
            Color::White => 7,
103
            Color::Color256(x) => x as usize,
104
        }
105
35184
    }
106

            
107
    #[inline]
108
35184
    fn is_color256(self) -> bool {
109
35184
        #[allow(clippy::match_like_matches_macro)]
110
35184
        match self {
111
            Color::Color256(_) => true,
112
35184
            _ => false,
113
        }
114
35184
    }
115
}
116

            
117
/// A terminal style attribute.
118
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
119
pub enum Attribute {
120
    Bold,
121
    Dim,
122
    Italic,
123
    Underlined,
124
    Blink,
125
    BlinkFast,
126
    Reverse,
127
    Hidden,
128
    StrikeThrough,
129
}
130

            
131
impl Attribute {
132
    #[inline]
133
    fn ansi_num(self) -> usize {
134
        match self {
135
            Attribute::Bold => 1,
136
            Attribute::Dim => 2,
137
            Attribute::Italic => 3,
138
            Attribute::Underlined => 4,
139
            Attribute::Blink => 5,
140
            Attribute::BlinkFast => 6,
141
            Attribute::Reverse => 7,
142
            Attribute::Hidden => 8,
143
            Attribute::StrikeThrough => 9,
144
        }
145
    }
146
}
147

            
148
/// Defines the alignment for padding operations.
149
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
150
pub enum Alignment {
151
    Left,
152
    Center,
153
    Right,
154
}
155

            
156
/// A stored style that can be applied.
157
#[derive(Clone, Debug, PartialEq, Eq)]
158
pub struct Style {
159
    fg: Option<Color>,
160
    bg: Option<Color>,
161
    fg_bright: bool,
162
    bg_bright: bool,
163
    attrs: BTreeSet<Attribute>,
164
    force: Option<bool>,
165
    for_stderr: bool,
166
}
167

            
168
impl Default for Style {
169
    fn default() -> Style {
170
        Style::new()
171
    }
172
}
173

            
174
impl Style {
175
    /// Returns an empty default style.
176
17596
    pub fn new() -> Style {
177
17596
        Style {
178
17596
            fg: None,
179
17596
            bg: None,
180
17596
            fg_bright: false,
181
17596
            bg_bright: false,
182
17596
            attrs: BTreeSet::new(),
183
17596
            force: None,
184
17596
            for_stderr: false,
185
17596
        }
186
17596
    }
187

            
188
    /// Creates a style from a dotted string.
189
    ///
190
    /// Effectively the string is split at each dot and then the
191
    /// terms in between are applied.  For instance `red.on_blue` will
192
    /// create a string that is red on blue background. `9.on_12` is
193
    /// the same, but using 256 color numbers. Unknown terms are
194
    /// ignored.
195
4
    pub fn from_dotted_str(s: &str) -> Style {
196
4
        let mut rv = Style::new();
197
4
        for part in s.split('.') {
198
4
            rv = match part {
199
4
                "black" => rv.black(),
200
4
                "red" => rv.red(),
201
4
                "green" => rv.green(),
202
4
                "yellow" => rv.yellow(),
203
4
                "blue" => rv.blue(),
204
2
                "magenta" => rv.magenta(),
205
2
                "cyan" => rv.cyan(),
206
                "white" => rv.white(),
207
                "bright" => rv.bright(),
208
                "on_black" => rv.on_black(),
209
                "on_red" => rv.on_red(),
210
                "on_green" => rv.on_green(),
211
                "on_yellow" => rv.on_yellow(),
212
                "on_blue" => rv.on_blue(),
213
                "on_magenta" => rv.on_magenta(),
214
                "on_cyan" => rv.on_cyan(),
215
                "on_white" => rv.on_white(),
216
                "on_bright" => rv.on_bright(),
217
                "bold" => rv.bold(),
218
                "dim" => rv.dim(),
219
                "underlined" => rv.underlined(),
220
                "blink" => rv.blink(),
221
                "blink_fast" => rv.blink_fast(),
222
                "reverse" => rv.reverse(),
223
                "hidden" => rv.hidden(),
224
                "strikethrough" => rv.strikethrough(),
225
                on_c if on_c.starts_with("on_") => {
226
                    if let Ok(n) = on_c[3..].parse::<u8>() {
227
                        rv.on_color256(n)
228
                    } else {
229
                        continue;
230
                    }
231
                }
232
                c => {
233
                    if let Ok(n) = c.parse::<u8>() {
234
                        rv.color256(n)
235
                    } else {
236
                        continue;
237
                    }
238
                }
239
            };
240
        }
241
4
        rv
242
4
    }
243

            
244
    /// Apply the style to something that can be displayed.
245
35184
    pub fn apply_to<D>(&self, val: D) -> StyledObject<D> {
246
35184
        StyledObject {
247
35184
            style: self.clone(),
248
35184
            val,
249
35184
        }
250
35184
    }
251

            
252
    /// Forces styling on or off.
253
    ///
254
    /// This overrides the automatic detection.
255
    #[inline]
256
    pub fn force_styling(mut self, value: bool) -> Style {
257
        self.force = Some(value);
258
        self
259
    }
260

            
261
    /// Specifies that style is applying to something being written on stderr.
262
    #[inline]
263
    pub fn for_stderr(mut self) -> Style {
264
        self.for_stderr = true;
265
        self
266
    }
267

            
268
    /// Specifies that style is applying to something being written on stdout.
269
    ///
270
    /// This is the default behaviour.
271
    #[inline]
272
    pub fn for_stdout(mut self) -> Style {
273
        self.for_stderr = false;
274
        self
275
    }
276

            
277
    /// Sets a foreground color.
278
    #[inline]
279
4
    pub fn fg(mut self, color: Color) -> Style {
280
4
        self.fg = Some(color);
281
4
        self
282
4
    }
283

            
284
    /// Sets a background color.
285
    #[inline]
286
    pub fn bg(mut self, color: Color) -> Style {
287
        self.bg = Some(color);
288
        self
289
    }
290

            
291
    /// Adds a attr.
292
    #[inline]
293
    pub fn attr(mut self, attr: Attribute) -> Style {
294
        self.attrs.insert(attr);
295
        self
296
    }
297

            
298
    #[inline]
299
    pub fn black(self) -> Style {
300
        self.fg(Color::Black)
301
    }
302
    #[inline]
303
    pub fn red(self) -> Style {
304
        self.fg(Color::Red)
305
    }
306
    #[inline]
307
    pub fn green(self) -> Style {
308
        self.fg(Color::Green)
309
    }
310
    #[inline]
311
    pub fn yellow(self) -> Style {
312
        self.fg(Color::Yellow)
313
    }
314
    #[inline]
315
2
    pub fn blue(self) -> Style {
316
2
        self.fg(Color::Blue)
317
2
    }
318
    #[inline]
319
    pub fn magenta(self) -> Style {
320
        self.fg(Color::Magenta)
321
    }
322
    #[inline]
323
2
    pub fn cyan(self) -> Style {
324
2
        self.fg(Color::Cyan)
325
2
    }
326
    #[inline]
327
    pub fn white(self) -> Style {
328
        self.fg(Color::White)
329
    }
330
    #[inline]
331
    pub fn color256(self, color: u8) -> Style {
332
        self.fg(Color::Color256(color))
333
    }
334

            
335
    #[inline]
336
    pub fn bright(mut self) -> Style {
337
        self.fg_bright = true;
338
        self
339
    }
340

            
341
    #[inline]
342
    pub fn on_black(self) -> Style {
343
        self.bg(Color::Black)
344
    }
345
    #[inline]
346
    pub fn on_red(self) -> Style {
347
        self.bg(Color::Red)
348
    }
349
    #[inline]
350
    pub fn on_green(self) -> Style {
351
        self.bg(Color::Green)
352
    }
353
    #[inline]
354
    pub fn on_yellow(self) -> Style {
355
        self.bg(Color::Yellow)
356
    }
357
    #[inline]
358
    pub fn on_blue(self) -> Style {
359
        self.bg(Color::Blue)
360
    }
361
    #[inline]
362
    pub fn on_magenta(self) -> Style {
363
        self.bg(Color::Magenta)
364
    }
365
    #[inline]
366
    pub fn on_cyan(self) -> Style {
367
        self.bg(Color::Cyan)
368
    }
369
    #[inline]
370
    pub fn on_white(self) -> Style {
371
        self.bg(Color::White)
372
    }
373
    #[inline]
374
    pub fn on_color256(self, color: u8) -> Style {
375
        self.bg(Color::Color256(color))
376
    }
377

            
378
    #[inline]
379
    pub fn on_bright(mut self) -> Style {
380
        self.bg_bright = true;
381
        self
382
    }
383

            
384
    #[inline]
385
    pub fn bold(self) -> Style {
386
        self.attr(Attribute::Bold)
387
    }
388
    #[inline]
389
    pub fn dim(self) -> Style {
390
        self.attr(Attribute::Dim)
391
    }
392
    #[inline]
393
    pub fn italic(self) -> Style {
394
        self.attr(Attribute::Italic)
395
    }
396
    #[inline]
397
    pub fn underlined(self) -> Style {
398
        self.attr(Attribute::Underlined)
399
    }
400
    #[inline]
401
    pub fn blink(self) -> Style {
402
        self.attr(Attribute::Blink)
403
    }
404
    #[inline]
405
    pub fn blink_fast(self) -> Style {
406
        self.attr(Attribute::BlinkFast)
407
    }
408
    #[inline]
409
    pub fn reverse(self) -> Style {
410
        self.attr(Attribute::Reverse)
411
    }
412
    #[inline]
413
    pub fn hidden(self) -> Style {
414
        self.attr(Attribute::Hidden)
415
    }
416
    #[inline]
417
    pub fn strikethrough(self) -> Style {
418
        self.attr(Attribute::StrikeThrough)
419
    }
420
}
421

            
422
/// Wraps an object for formatting for styling.
423
///
424
/// Example:
425
///
426
/// ```rust,no_run
427
/// # use console::style;
428
/// format!("Hello {}", style("World").cyan());
429
/// ```
430
///
431
/// This is a shortcut for making a new style and applying it
432
/// to a value:
433
///
434
/// ```rust,no_run
435
/// # use console::Style;
436
/// format!("Hello {}", Style::new().cyan().apply_to("World"));
437
/// ```
438
pub fn style<D>(val: D) -> StyledObject<D> {
439
    Style::new().apply_to(val)
440
}
441

            
442
/// A formatting wrapper that can be styled for a terminal.
443
#[derive(Clone)]
444
pub struct StyledObject<D> {
445
    style: Style,
446
    val: D,
447
}
448

            
449
impl<D> StyledObject<D> {
450
    /// Forces styling on or off.
451
    ///
452
    /// This overrides the automatic detection.
453
    #[inline]
454
    pub fn force_styling(mut self, value: bool) -> StyledObject<D> {
455
        self.style = self.style.force_styling(value);
456
        self
457
    }
458

            
459
    /// Specifies that style is applying to something being written on stderr
460
    #[inline]
461
    pub fn for_stderr(mut self) -> StyledObject<D> {
462
        self.style = self.style.for_stderr();
463
        self
464
    }
465

            
466
    /// Specifies that style is applying to something being written on stdout
467
    ///
468
    /// This is the default
469
    #[inline]
470
    pub fn for_stdout(mut self) -> StyledObject<D> {
471
        self.style = self.style.for_stdout();
472
        self
473
    }
474

            
475
    /// Sets a foreground color.
476
    #[inline]
477
    pub fn fg(mut self, color: Color) -> StyledObject<D> {
478
        self.style = self.style.fg(color);
479
        self
480
    }
481

            
482
    /// Sets a background color.
483
    #[inline]
484
    pub fn bg(mut self, color: Color) -> StyledObject<D> {
485
        self.style = self.style.bg(color);
486
        self
487
    }
488

            
489
    /// Adds a attr.
490
    #[inline]
491
    pub fn attr(mut self, attr: Attribute) -> StyledObject<D> {
492
        self.style = self.style.attr(attr);
493
        self
494
    }
495

            
496
    #[inline]
497
    pub fn black(self) -> StyledObject<D> {
498
        self.fg(Color::Black)
499
    }
500
    #[inline]
501
    pub fn red(self) -> StyledObject<D> {
502
        self.fg(Color::Red)
503
    }
504
    #[inline]
505
    pub fn green(self) -> StyledObject<D> {
506
        self.fg(Color::Green)
507
    }
508
    #[inline]
509
    pub fn yellow(self) -> StyledObject<D> {
510
        self.fg(Color::Yellow)
511
    }
512
    #[inline]
513
    pub fn blue(self) -> StyledObject<D> {
514
        self.fg(Color::Blue)
515
    }
516
    #[inline]
517
    pub fn magenta(self) -> StyledObject<D> {
518
        self.fg(Color::Magenta)
519
    }
520
    #[inline]
521
    pub fn cyan(self) -> StyledObject<D> {
522
        self.fg(Color::Cyan)
523
    }
524
    #[inline]
525
    pub fn white(self) -> StyledObject<D> {
526
        self.fg(Color::White)
527
    }
528
    #[inline]
529
    pub fn color256(self, color: u8) -> StyledObject<D> {
530
        self.fg(Color::Color256(color))
531
    }
532

            
533
    #[inline]
534
    pub fn bright(mut self) -> StyledObject<D> {
535
        self.style = self.style.bright();
536
        self
537
    }
538

            
539
    #[inline]
540
    pub fn on_black(self) -> StyledObject<D> {
541
        self.bg(Color::Black)
542
    }
543
    #[inline]
544
    pub fn on_red(self) -> StyledObject<D> {
545
        self.bg(Color::Red)
546
    }
547
    #[inline]
548
    pub fn on_green(self) -> StyledObject<D> {
549
        self.bg(Color::Green)
550
    }
551
    #[inline]
552
    pub fn on_yellow(self) -> StyledObject<D> {
553
        self.bg(Color::Yellow)
554
    }
555
    #[inline]
556
    pub fn on_blue(self) -> StyledObject<D> {
557
        self.bg(Color::Blue)
558
    }
559
    #[inline]
560
    pub fn on_magenta(self) -> StyledObject<D> {
561
        self.bg(Color::Magenta)
562
    }
563
    #[inline]
564
    pub fn on_cyan(self) -> StyledObject<D> {
565
        self.bg(Color::Cyan)
566
    }
567
    #[inline]
568
    pub fn on_white(self) -> StyledObject<D> {
569
        self.bg(Color::White)
570
    }
571
    #[inline]
572
    pub fn on_color256(self, color: u8) -> StyledObject<D> {
573
        self.bg(Color::Color256(color))
574
    }
575

            
576
    #[inline]
577
    pub fn on_bright(mut self) -> StyledObject<D> {
578
        self.style = self.style.on_bright();
579
        self
580
    }
581

            
582
    #[inline]
583
    pub fn bold(self) -> StyledObject<D> {
584
        self.attr(Attribute::Bold)
585
    }
586
    #[inline]
587
    pub fn dim(self) -> StyledObject<D> {
588
        self.attr(Attribute::Dim)
589
    }
590
    #[inline]
591
    pub fn italic(self) -> StyledObject<D> {
592
        self.attr(Attribute::Italic)
593
    }
594
    #[inline]
595
    pub fn underlined(self) -> StyledObject<D> {
596
        self.attr(Attribute::Underlined)
597
    }
598
    #[inline]
599
    pub fn blink(self) -> StyledObject<D> {
600
        self.attr(Attribute::Blink)
601
    }
602
    #[inline]
603
    pub fn blink_fast(self) -> StyledObject<D> {
604
        self.attr(Attribute::BlinkFast)
605
    }
606
    #[inline]
607
    pub fn reverse(self) -> StyledObject<D> {
608
        self.attr(Attribute::Reverse)
609
    }
610
    #[inline]
611
    pub fn hidden(self) -> StyledObject<D> {
612
        self.attr(Attribute::Hidden)
613
    }
614
    #[inline]
615
    pub fn strikethrough(self) -> StyledObject<D> {
616
        self.attr(Attribute::StrikeThrough)
617
    }
618
}
619

            
620
macro_rules! impl_fmt {
621
    ($name:ident) => {
622
        impl<D: fmt::$name> fmt::$name for StyledObject<D> {
623
35184
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
624
35184
                let mut reset = false;
625
35184
                if self
626
35184
                    .style
627
35184
                    .force
628
35184
                    .unwrap_or_else(|| match self.style.for_stderr {
629
                        true => colors_enabled_stderr(),
630
35184
                        false => colors_enabled(),
631
35184
                    })
632
                {
633
35184
                    if let Some(fg) = self.style.fg {
634
35184
                        if fg.is_color256() {
635
                            write!(f, "\x1b[38;5;{}m", fg.ansi_num())?;
636
35184
                        } else if self.style.fg_bright {
637
                            write!(f, "\x1b[38;5;{}m", fg.ansi_num() + 8)?;
638
                        } else {
639
35184
                            write!(f, "\x1b[{}m", fg.ansi_num() + 30)?;
640
                        }
641
35184
                        reset = true;
642
                    }
643
35184
                    if let Some(bg) = self.style.bg {
644
                        if bg.is_color256() {
645
                            write!(f, "\x1b[48;5;{}m", bg.ansi_num())?;
646
                        } else if self.style.bg_bright {
647
                            write!(f, "\x1b[48;5;{}m", bg.ansi_num() + 8)?;
648
                        } else {
649
                            write!(f, "\x1b[{}m", bg.ansi_num() + 40)?;
650
                        }
651
                        reset = true;
652
35184
                    }
653
35184
                    for attr in &self.style.attrs {
654
                        write!(f, "\x1b[{}m", attr.ansi_num())?;
655
                        reset = true;
656
                    }
657
                }
658
35184
                fmt::$name::fmt(&self.val, f)?;
659
35184
                if reset {
660
35184
                    write!(f, "\x1b[0m")?;
661
                }
662
35184
                Ok(())
663
35184
            }
664
        }
665
    };
666
}
667

            
668
impl_fmt!(Binary);
669
impl_fmt!(Debug);
670
impl_fmt!(Display);
671
impl_fmt!(LowerExp);
672
impl_fmt!(LowerHex);
673
impl_fmt!(Octal);
674
impl_fmt!(Pointer);
675
impl_fmt!(UpperExp);
676
impl_fmt!(UpperHex);
677

            
678
/// "Intelligent" emoji formatter.
679
///
680
/// This struct intelligently wraps an emoji so that it is rendered
681
/// only on systems that want emojis and renders a fallback on others.
682
///
683
/// Example:
684
///
685
/// ```rust
686
/// use console::Emoji;
687
/// println!("[3/4] {}Downloading ...", Emoji("🚚 ", ""));
688
/// println!("[4/4] {} Done!", Emoji("✨", ":-)"));
689
/// ```
690
#[derive(Copy, Clone)]
691
pub struct Emoji<'a, 'b>(pub &'a str, pub &'b str);
692

            
693
impl<'a, 'b> Emoji<'a, 'b> {
694
    pub fn new(emoji: &'a str, fallback: &'b str) -> Emoji<'a, 'b> {
695
        Emoji(emoji, fallback)
696
    }
697
}
698

            
699
impl<'a, 'b> fmt::Display for Emoji<'a, 'b> {
700
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
701
        if wants_emoji() {
702
            write!(f, "{}", self.0)
703
        } else {
704
            write!(f, "{}", self.1)
705
        }
706
    }
707
}
708

            
709
70368
fn str_width(s: &str) -> usize {
710
    #[cfg(feature = "unicode-width")]
711
    {
712
        use unicode_width::UnicodeWidthStr;
713
70368
        s.width()
714
70368
    }
715
70368
    #[cfg(not(feature = "unicode-width"))]
716
70368
    {
717
70368
        s.chars().count()
718
70368
    }
719
70368
}
720

            
721
#[cfg(feature = "ansi-parsing")]
722
fn char_width(c: char) -> usize {
723
    #[cfg(feature = "unicode-width")]
724
    {
725
        use unicode_width::UnicodeWidthChar;
726
        c.width().unwrap_or(0)
727
    }
728
    #[cfg(not(feature = "unicode-width"))]
729
    {
730
        let _c = c;
731
        1
732
    }
733
}
734

            
735
/// Truncates a string to a certain number of characters.
736
///
737
/// This ensures that escape codes are not screwed up in the process.
738
/// If the maximum length is hit the string will be truncated but
739
/// escapes code will still be honored.  If truncation takes place
740
/// the tail string will be appended.
741
pub fn truncate_str<'a>(s: &'a str, width: usize, tail: &str) -> Cow<'a, str> {
742
    #[cfg(feature = "ansi-parsing")]
743
    {
744
        use std::cmp::Ordering;
745
        let mut iter = AnsiCodeIterator::new(s);
746
        let mut length = 0;
747
        let mut rv = None;
748

            
749
        while let Some(item) = iter.next() {
750
            match item {
751
                (s, false) => {
752
                    if rv.is_none() {
753
                        if str_width(s) + length > width - str_width(tail) {
754
                            let ts = iter.current_slice();
755

            
756
                            let mut s_byte = 0;
757
                            let mut s_width = 0;
758
                            let rest_width = width - str_width(tail) - length;
759
                            for c in s.chars() {
760
                                s_byte += c.len_utf8();
761
                                s_width += char_width(c);
762
                                match s_width.cmp(&rest_width) {
763
                                    Ordering::Equal => break,
764
                                    Ordering::Greater => {
765
                                        s_byte -= c.len_utf8();
766
                                        break;
767
                                    }
768
                                    Ordering::Less => continue,
769
                                }
770
                            }
771

            
772
                            let idx = ts.len() - s.len() + s_byte;
773
                            let mut buf = ts[..idx].to_string();
774
                            buf.push_str(tail);
775
                            rv = Some(buf);
776
                        }
777
                        length += str_width(s);
778
                    }
779
                }
780
                (s, true) => {
781
                    if let Some(ref mut rv) = rv {
782
                        rv.push_str(s);
783
                    }
784
                }
785
            }
786
        }
787

            
788
        if let Some(buf) = rv {
789
            Cow::Owned(buf)
790
        } else {
791
            Cow::Borrowed(s)
792
        }
793
    }
794

            
795
    #[cfg(not(feature = "ansi-parsing"))]
796
    {
797
        if s.len() <= width - tail.len() {
798
            Cow::Borrowed(s)
799
        } else {
800
            Cow::Owned(format!(
801
                "{}{}",
802
                s.get(..width - tail.len()).unwrap_or_default(),
803
                tail
804
            ))
805
        }
806
    }
807
}
808

            
809
/// Pads a string to fill a certain number of characters.
810
///
811
/// This will honor ansi codes correctly and allows you to align a string
812
/// on the left, right or centered.  Additionally truncation can be enabled
813
/// by setting `truncate` to a string that should be used as a truncation
814
/// marker.
815
pub fn pad_str<'a>(
816
    s: &'a str,
817
    width: usize,
818
    align: Alignment,
819
    truncate: Option<&str>,
820
) -> Cow<'a, str> {
821
    pad_str_with(s, width, align, truncate, ' ')
822
}
823
/// Pads a string with specific padding to fill a certain number of characters.
824
///
825
/// This will honor ansi codes correctly and allows you to align a string
826
/// on the left, right or centered.  Additionally truncation can be enabled
827
/// by setting `truncate` to a string that should be used as a truncation
828
/// marker.
829
pub fn pad_str_with<'a>(
830
    s: &'a str,
831
    width: usize,
832
    align: Alignment,
833
    truncate: Option<&str>,
834
    pad: char,
835
) -> Cow<'a, str> {
836
    let cols = measure_text_width(s);
837

            
838
    if cols >= width {
839
        return match truncate {
840
            None => Cow::Borrowed(s),
841
            Some(tail) => truncate_str(s, width, tail),
842
        };
843
    }
844

            
845
    let diff = width - cols;
846

            
847
    let (left_pad, right_pad) = match align {
848
        Alignment::Left => (0, diff),
849
        Alignment::Right => (diff, 0),
850
        Alignment::Center => (diff / 2, diff - diff / 2),
851
    };
852

            
853
    let mut rv = String::new();
854
    for _ in 0..left_pad {
855
        rv.push(pad);
856
    }
857
    rv.push_str(s);
858
    for _ in 0..right_pad {
859
        rv.push(pad);
860
    }
861
    Cow::Owned(rv)
862
}
863

            
864
#[test]
865
fn test_text_width() {
866
    let s = style("foo")
867
        .red()
868
        .on_black()
869
        .bold()
870
        .force_styling(true)
871
        .to_string();
872
    assert_eq!(
873
        measure_text_width(&s),
874
        if cfg!(feature = "ansi-parsing") {
875
            3
876
        } else if cfg!(feature = "unicode-width") {
877
            17
878
        } else {
879
            21
880
        }
881
    );
882
}
883

            
884
#[test]
885
#[cfg(all(feature = "unicode-width", feature = "ansi-parsing"))]
886
fn test_truncate_str() {
887
    let s = format!("foo {}", style("bar").red().force_styling(true));
888
    assert_eq!(
889
        &truncate_str(&s, 5, ""),
890
        &format!("foo {}", style("b").red().force_styling(true))
891
    );
892
    let s = format!("foo {}", style("bar").red().force_styling(true));
893
    assert_eq!(
894
        &truncate_str(&s, 5, "!"),
895
        &format!("foo {}", style("!").red().force_styling(true))
896
    );
897
    let s = format!("foo {} baz", style("bar").red().force_styling(true));
898
    assert_eq!(
899
        &truncate_str(&s, 10, "..."),
900
        &format!("foo {}...", style("bar").red().force_styling(true))
901
    );
902
    let s = format!("foo {}", style("バー").red().force_styling(true));
903
    assert_eq!(
904
        &truncate_str(&s, 5, ""),
905
        &format!("foo {}", style("").red().force_styling(true))
906
    );
907
    let s = format!("foo {}", style("バー").red().force_styling(true));
908
    assert_eq!(
909
        &truncate_str(&s, 6, ""),
910
        &format!("foo {}", style("バ").red().force_styling(true))
911
    );
912
}
913

            
914
#[test]
915
fn test_truncate_str_no_ansi() {
916
    assert_eq!(&truncate_str("foo bar", 5, ""), "foo b");
917
    assert_eq!(&truncate_str("foo bar", 5, "!"), "foo !");
918
    assert_eq!(&truncate_str("foo bar baz", 10, "..."), "foo bar...");
919
}
920

            
921
#[test]
922
fn test_pad_str() {
923
    assert_eq!(pad_str("foo", 7, Alignment::Center, None), "  foo  ");
924
    assert_eq!(pad_str("foo", 7, Alignment::Left, None), "foo    ");
925
    assert_eq!(pad_str("foo", 7, Alignment::Right, None), "    foo");
926
    assert_eq!(pad_str("foo", 3, Alignment::Left, None), "foo");
927
    assert_eq!(pad_str("foobar", 3, Alignment::Left, None), "foobar");
928
    assert_eq!(pad_str("foobar", 3, Alignment::Left, Some("")), "foo");
929
    assert_eq!(
930
        pad_str("foobarbaz", 6, Alignment::Left, Some("...")),
931
        "foo..."
932
    );
933
}
934

            
935
#[test]
936
fn test_pad_str_with() {
937
    assert_eq!(
938
        pad_str_with("foo", 7, Alignment::Center, None, '#'),
939
        "##foo##"
940
    );
941
    assert_eq!(
942
        pad_str_with("foo", 7, Alignment::Left, None, '#'),
943
        "foo####"
944
    );
945
    assert_eq!(
946
        pad_str_with("foo", 7, Alignment::Right, None, '#'),
947
        "####foo"
948
    );
949
    assert_eq!(pad_str_with("foo", 3, Alignment::Left, None, '#'), "foo");
950
    assert_eq!(
951
        pad_str_with("foobar", 3, Alignment::Left, None, '#'),
952
        "foobar"
953
    );
954
    assert_eq!(
955
        pad_str_with("foobar", 3, Alignment::Left, Some(""), '#'),
956
        "foo"
957
    );
958
    assert_eq!(
959
        pad_str_with("foobarbaz", 6, Alignment::Left, Some("..."), '#'),
960
        "foo..."
961
    );
962
}