]> git.proxmox.com Git - rustc.git/blame - vendor/console/src/utils.rs
New upstream version 1.46.0+dfsg1
[rustc.git] / vendor / console / src / utils.rs
CommitLineData
f035d41b
XL
1use std::borrow::Cow;
2use std::collections::BTreeSet;
3use std::env;
4use std::fmt;
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use crate::term::{wants_emoji, Term};
8use lazy_static::lazy_static;
9
10#[cfg(feature = "ansi-parsing")]
11use crate::ansi::{strip_ansi_codes, AnsiCodeIterator};
12
13#[cfg(not(feature = "ansi-parsing"))]
14fn strip_ansi_codes(s: &str) -> &str {
15 s
16}
17
18fn default_colors_enabled(out: &Term) -> bool {
19 (out.features().colors_supported() && &env::var("CLICOLOR").unwrap_or("1".into()) != "0")
20 || &env::var("CLICOLOR_FORCE").unwrap_or("0".into()) != "0"
21}
22
23lazy_static! {
24 static ref STDOUT_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stdout()));
25 static ref STDERR_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stderr()));
26}
27
28/// Returns `true` if colors should be enabled for stdout.
29///
30/// This honors the [clicolors spec](http://bixense.com/clicolors/).
31///
32/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
33/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
34/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
35#[inline]
36pub fn colors_enabled() -> bool {
37 STDOUT_COLORS.load(Ordering::Relaxed)
38}
39
40/// Forces colorization on or off for stdout.
41///
42/// This overrides the default for the current process and changes the return value of the
43/// `colors_enabled` function.
44#[inline]
45pub fn set_colors_enabled(val: bool) {
46 STDOUT_COLORS.store(val, Ordering::Relaxed)
47}
48
49/// Returns `true` if colors should be enabled for stderr.
50///
51/// This honors the [clicolors spec](http://bixense.com/clicolors/).
52///
53/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
54/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
55/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
56#[inline]
57pub fn colors_enabled_stderr() -> bool {
58 STDERR_COLORS.load(Ordering::Relaxed)
59}
60
61/// Forces colorization on or off for stderr.
62///
63/// This overrides the default for the current process and changes the return value of the
64/// `colors_enabled` function.
65#[inline]
66pub fn set_colors_enabled_stderr(val: bool) {
67 STDERR_COLORS.store(val, Ordering::Relaxed)
68}
69
70/// Measure the width of a string in terminal characters.
71pub fn measure_text_width(s: &str) -> usize {
72 str_width(&strip_ansi_codes(s))
73}
74
75/// A terminal color.
76#[derive(Copy, Clone, Debug, PartialEq, Eq)]
77pub enum Color {
78 Black,
79 Red,
80 Green,
81 Yellow,
82 Blue,
83 Magenta,
84 Cyan,
85 White,
86}
87
88impl Color {
89 #[inline]
90 fn ansi_num(self) -> usize {
91 match self {
92 Color::Black => 0,
93 Color::Red => 1,
94 Color::Green => 2,
95 Color::Yellow => 3,
96 Color::Blue => 4,
97 Color::Magenta => 5,
98 Color::Cyan => 6,
99 Color::White => 7,
100 }
101 }
102}
103
104/// A terminal style attribute.
105#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
106pub enum Attribute {
107 Bold,
108 Dim,
109 Italic,
110 Underlined,
111 Blink,
112 Reverse,
113 Hidden,
114}
115
116impl Attribute {
117 #[inline]
118 fn ansi_num(self) -> usize {
119 match self {
120 Attribute::Bold => 1,
121 Attribute::Dim => 2,
122 Attribute::Italic => 3,
123 Attribute::Underlined => 4,
124 Attribute::Blink => 5,
125 Attribute::Reverse => 7,
126 Attribute::Hidden => 8,
127 }
128 }
129}
130
131/// Defines the alignment for padding operations.
132#[derive(Copy, Clone, Debug, PartialEq, Eq)]
133pub enum Alignment {
134 Left,
135 Center,
136 Right,
137}
138
139/// A stored style that can be applied.
140#[derive(Clone, Debug, PartialEq, Eq)]
141pub struct Style {
142 fg: Option<Color>,
143 bg: Option<Color>,
144 fg_bright: bool,
145 bg_bright: bool,
146 attrs: BTreeSet<Attribute>,
147 force: Option<bool>,
148 for_stderr: bool,
149}
150
151impl Default for Style {
152 fn default() -> Style {
153 Style::new()
154 }
155}
156
157impl Style {
158 /// Returns an empty default style.
159 pub fn new() -> Style {
160 Style {
161 fg: None,
162 bg: None,
163 fg_bright: false,
164 bg_bright: false,
165 attrs: BTreeSet::new(),
166 force: None,
167 for_stderr: false,
168 }
169 }
170
171 /// Creates a style from a dotted string.
172 ///
173 /// Effectively the string is split at each dot and then the
174 /// terms in between are applied. For instance `red.on_blue` will
175 /// create a string that is red on blue background. Unknown terms
176 /// are ignored.
177 pub fn from_dotted_str(s: &str) -> Style {
178 let mut rv = Style::new();
179 for part in s.split('.') {
180 rv = match part {
181 "black" => rv.black(),
182 "red" => rv.red(),
183 "green" => rv.green(),
184 "yellow" => rv.yellow(),
185 "blue" => rv.blue(),
186 "magenta" => rv.magenta(),
187 "cyan" => rv.cyan(),
188 "white" => rv.white(),
189 "bright" => rv.bright(),
190 "on_black" => rv.on_black(),
191 "on_red" => rv.on_red(),
192 "on_green" => rv.on_green(),
193 "on_yellow" => rv.on_yellow(),
194 "on_blue" => rv.on_blue(),
195 "on_magenta" => rv.on_magenta(),
196 "on_cyan" => rv.on_cyan(),
197 "on_white" => rv.on_white(),
198 "on_bright" => rv.on_bright(),
199 "bold" => rv.bold(),
200 "dim" => rv.dim(),
201 "underlined" => rv.underlined(),
202 "blink" => rv.blink(),
203 "reverse" => rv.reverse(),
204 "hidden" => rv.hidden(),
205 _ => {
206 continue;
207 }
208 };
209 }
210 rv
211 }
212
213 /// Apply the style to something that can be displayed.
214 pub fn apply_to<D>(&self, val: D) -> StyledObject<D> {
215 StyledObject {
216 style: self.clone(),
217 val,
218 }
219 }
220
221 /// Forces styling on or off.
222 ///
223 /// This overrides the detection from `clicolors-control`.
224 #[inline]
225 pub fn force_styling(mut self, value: bool) -> Style {
226 self.force = Some(value);
227 self
228 }
229
230 /// Specifies that style is applying to something being written on stderr.
231 #[inline]
232 pub fn for_stderr(mut self) -> Style {
233 self.for_stderr = true;
234 self
235 }
236
237 /// Specifies that style is applying to something being written on stdout.
238 ///
239 /// This is the default behaviour.
240 #[inline]
241 pub fn for_stdout(mut self) -> Style {
242 self.for_stderr = false;
243 self
244 }
245
246 /// Sets a foreground color.
247 #[inline]
248 pub fn fg(mut self, color: Color) -> Style {
249 self.fg = Some(color);
250 self
251 }
252
253 /// Sets a background color.
254 #[inline]
255 pub fn bg(mut self, color: Color) -> Style {
256 self.bg = Some(color);
257 self
258 }
259
260 /// Adds a attr.
261 #[inline]
262 pub fn attr(mut self, attr: Attribute) -> Style {
263 self.attrs.insert(attr);
264 self
265 }
266
267 #[inline]
268 pub fn black(self) -> Style {
269 self.fg(Color::Black)
270 }
271 #[inline]
272 pub fn red(self) -> Style {
273 self.fg(Color::Red)
274 }
275 #[inline]
276 pub fn green(self) -> Style {
277 self.fg(Color::Green)
278 }
279 #[inline]
280 pub fn yellow(self) -> Style {
281 self.fg(Color::Yellow)
282 }
283 #[inline]
284 pub fn blue(self) -> Style {
285 self.fg(Color::Blue)
286 }
287 #[inline]
288 pub fn magenta(self) -> Style {
289 self.fg(Color::Magenta)
290 }
291 #[inline]
292 pub fn cyan(self) -> Style {
293 self.fg(Color::Cyan)
294 }
295 #[inline]
296 pub fn white(self) -> Style {
297 self.fg(Color::White)
298 }
299
300 #[inline]
301 pub fn bright(mut self) -> Style {
302 self.fg_bright = true;
303 self
304 }
305
306 #[inline]
307 pub fn on_black(self) -> Style {
308 self.bg(Color::Black)
309 }
310 #[inline]
311 pub fn on_red(self) -> Style {
312 self.bg(Color::Red)
313 }
314 #[inline]
315 pub fn on_green(self) -> Style {
316 self.bg(Color::Green)
317 }
318 #[inline]
319 pub fn on_yellow(self) -> Style {
320 self.bg(Color::Yellow)
321 }
322 #[inline]
323 pub fn on_blue(self) -> Style {
324 self.bg(Color::Blue)
325 }
326 #[inline]
327 pub fn on_magenta(self) -> Style {
328 self.bg(Color::Magenta)
329 }
330 #[inline]
331 pub fn on_cyan(self) -> Style {
332 self.bg(Color::Cyan)
333 }
334 #[inline]
335 pub fn on_white(self) -> Style {
336 self.bg(Color::White)
337 }
338
339 #[inline]
340 pub fn on_bright(mut self) -> Style {
341 self.bg_bright = true;
342 self
343 }
344
345 #[inline]
346 pub fn bold(self) -> Style {
347 self.attr(Attribute::Bold)
348 }
349 #[inline]
350 pub fn dim(self) -> Style {
351 self.attr(Attribute::Dim)
352 }
353 #[inline]
354 pub fn italic(self) -> Style {
355 self.attr(Attribute::Italic)
356 }
357 #[inline]
358 pub fn underlined(self) -> Style {
359 self.attr(Attribute::Underlined)
360 }
361 #[inline]
362 pub fn blink(self) -> Style {
363 self.attr(Attribute::Blink)
364 }
365 #[inline]
366 pub fn reverse(self) -> Style {
367 self.attr(Attribute::Reverse)
368 }
369 #[inline]
370 pub fn hidden(self) -> Style {
371 self.attr(Attribute::Hidden)
372 }
373}
374
375/// Wraps an object for formatting for styling.
376///
377/// Example:
378///
379/// ```rust,no_run
380/// # use console::style;
381/// format!("Hello {}", style("World").cyan());
382/// ```
383///
384/// This is a shortcut for making a new style and applying it
385/// to a value:
386///
387/// ```rust,no_run
388/// # use console::Style;
389/// format!("Hello {}", Style::new().cyan().apply_to("World"));
390/// ```
391pub fn style<D>(val: D) -> StyledObject<D> {
392 Style::new().apply_to(val)
393}
394
395/// A formatting wrapper that can be styled for a terminal.
396#[derive(Clone)]
397pub struct StyledObject<D> {
398 style: Style,
399 val: D,
400}
401
402impl<D> StyledObject<D> {
403 /// Forces styling on or off.
404 ///
405 /// This overrides the detection from `clicolors-control`.
406 #[inline]
407 pub fn force_styling(mut self, value: bool) -> StyledObject<D> {
408 self.style = self.style.force_styling(value);
409 self
410 }
411
412 /// Specifies that style is applying to something being written on stderr
413 #[inline]
414 pub fn for_stderr(mut self) -> StyledObject<D> {
415 self.style = self.style.for_stderr();
416 self
417 }
418
419 /// Specifies that style is applying to something being written on stdout
420 ///
421 /// This is the default
422 #[inline]
423 pub fn for_stdout(mut self) -> StyledObject<D> {
424 self.style = self.style.for_stdout();
425 self
426 }
427
428 /// Sets a foreground color.
429 #[inline]
430 pub fn fg(mut self, color: Color) -> StyledObject<D> {
431 self.style = self.style.fg(color);
432 self
433 }
434
435 /// Sets a background color.
436 #[inline]
437 pub fn bg(mut self, color: Color) -> StyledObject<D> {
438 self.style = self.style.bg(color);
439 self
440 }
441
442 /// Adds a attr.
443 #[inline]
444 pub fn attr(mut self, attr: Attribute) -> StyledObject<D> {
445 self.style = self.style.attr(attr);
446 self
447 }
448
449 #[inline]
450 pub fn black(self) -> StyledObject<D> {
451 self.fg(Color::Black)
452 }
453 #[inline]
454 pub fn red(self) -> StyledObject<D> {
455 self.fg(Color::Red)
456 }
457 #[inline]
458 pub fn green(self) -> StyledObject<D> {
459 self.fg(Color::Green)
460 }
461 #[inline]
462 pub fn yellow(self) -> StyledObject<D> {
463 self.fg(Color::Yellow)
464 }
465 #[inline]
466 pub fn blue(self) -> StyledObject<D> {
467 self.fg(Color::Blue)
468 }
469 #[inline]
470 pub fn magenta(self) -> StyledObject<D> {
471 self.fg(Color::Magenta)
472 }
473 #[inline]
474 pub fn cyan(self) -> StyledObject<D> {
475 self.fg(Color::Cyan)
476 }
477 #[inline]
478 pub fn white(self) -> StyledObject<D> {
479 self.fg(Color::White)
480 }
481
482 #[inline]
483 pub fn bright(mut self) -> StyledObject<D> {
484 self.style = self.style.bright();
485 self
486 }
487
488 #[inline]
489 pub fn on_black(self) -> StyledObject<D> {
490 self.bg(Color::Black)
491 }
492 #[inline]
493 pub fn on_red(self) -> StyledObject<D> {
494 self.bg(Color::Red)
495 }
496 #[inline]
497 pub fn on_green(self) -> StyledObject<D> {
498 self.bg(Color::Green)
499 }
500 #[inline]
501 pub fn on_yellow(self) -> StyledObject<D> {
502 self.bg(Color::Yellow)
503 }
504 #[inline]
505 pub fn on_blue(self) -> StyledObject<D> {
506 self.bg(Color::Blue)
507 }
508 #[inline]
509 pub fn on_magenta(self) -> StyledObject<D> {
510 self.bg(Color::Magenta)
511 }
512 #[inline]
513 pub fn on_cyan(self) -> StyledObject<D> {
514 self.bg(Color::Cyan)
515 }
516 #[inline]
517 pub fn on_white(self) -> StyledObject<D> {
518 self.bg(Color::White)
519 }
520
521 #[inline]
522 pub fn on_bright(mut self) -> StyledObject<D> {
523 self.style = self.style.on_bright();
524 self
525 }
526
527 #[inline]
528 pub fn bold(self) -> StyledObject<D> {
529 self.attr(Attribute::Bold)
530 }
531 #[inline]
532 pub fn dim(self) -> StyledObject<D> {
533 self.attr(Attribute::Dim)
534 }
535 #[inline]
536 pub fn italic(self) -> StyledObject<D> {
537 self.attr(Attribute::Italic)
538 }
539 #[inline]
540 pub fn underlined(self) -> StyledObject<D> {
541 self.attr(Attribute::Underlined)
542 }
543 #[inline]
544 pub fn blink(self) -> StyledObject<D> {
545 self.attr(Attribute::Blink)
546 }
547 #[inline]
548 pub fn reverse(self) -> StyledObject<D> {
549 self.attr(Attribute::Reverse)
550 }
551 #[inline]
552 pub fn hidden(self) -> StyledObject<D> {
553 self.attr(Attribute::Hidden)
554 }
555}
556
557macro_rules! impl_fmt {
558 ($name:ident) => {
559 impl<D: fmt::$name> fmt::$name for StyledObject<D> {
560 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
561 let mut reset = false;
562 if self
563 .style
564 .force
565 .unwrap_or_else(|| match self.style.for_stderr {
566 true => colors_enabled_stderr(),
567 false => colors_enabled(),
568 })
569 {
570 if let Some(fg) = self.style.fg {
571 if self.style.fg_bright {
572 write!(f, "\x1b[38;5;{}m", fg.ansi_num() + 8)?;
573 } else {
574 write!(f, "\x1b[{}m", fg.ansi_num() + 30)?;
575 }
576 reset = true;
577 }
578 if let Some(bg) = self.style.bg {
579 if self.style.bg_bright {
580 write!(f, "\x1b[48;5;{}m", bg.ansi_num() + 8)?;
581 } else {
582 write!(f, "\x1b[{}m", bg.ansi_num() + 40)?;
583 }
584 reset = true;
585 }
586 for attr in &self.style.attrs {
587 write!(f, "\x1b[{}m", attr.ansi_num())?;
588 reset = true;
589 }
590 }
591 fmt::$name::fmt(&self.val, f)?;
592 if reset {
593 write!(f, "\x1b[0m")?;
594 }
595 Ok(())
596 }
597 }
598 };
599}
600
601impl_fmt!(Binary);
602impl_fmt!(Debug);
603impl_fmt!(Display);
604impl_fmt!(LowerExp);
605impl_fmt!(LowerHex);
606impl_fmt!(Octal);
607impl_fmt!(Pointer);
608impl_fmt!(UpperExp);
609impl_fmt!(UpperHex);
610
611/// "Intelligent" emoji formatter.
612///
613/// This struct intelligently wraps an emoji so that it is rendered
614/// only on systems that want emojis and renders a fallback on others.
615///
616/// Example:
617///
618/// ```rust
619/// use console::Emoji;
620/// println!("[3/4] {}Downloading ...", Emoji("🚚 ", ""));
621/// println!("[4/4] {} Done!", Emoji("✨", ":-)"));
622/// ```
623#[derive(Copy, Clone)]
624pub struct Emoji<'a, 'b>(pub &'a str, pub &'b str);
625
626impl<'a, 'b> Emoji<'a, 'b> {
627 pub fn new(emoji: &'a str, fallback: &'b str) -> Emoji<'a, 'b> {
628 Emoji(emoji, fallback)
629 }
630}
631
632impl<'a, 'b> fmt::Display for Emoji<'a, 'b> {
633 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
634 if wants_emoji() {
635 write!(f, "{}", self.0)
636 } else {
637 write!(f, "{}", self.1)
638 }
639 }
640}
641
642fn str_width(s: &str) -> usize {
643 #[cfg(feature = "unicode-width")]
644 {
645 use unicode_width::UnicodeWidthStr;
646 s.width()
647 }
648 #[cfg(not(feature = "unicode-width"))]
649 {
650 s.chars().count()
651 }
652}
653
654#[cfg(feature = "ansi-parsing")]
655fn char_width(c: char) -> usize {
656 #[cfg(feature = "unicode-width")]
657 {
658 use unicode_width::UnicodeWidthChar;
659 c.width().unwrap_or(0)
660 }
661 #[cfg(not(feature = "unicode-width"))]
662 {
663 let _c = c;
664 1
665 }
666}
667
668/// Truncates a string to a certain number of characters.
669///
670/// This ensures that escape codes are not screwed up in the process.
671/// If the maximum length is hit the string will be truncated but
672/// escapes code will still be honored. If truncation takes place
673/// the tail string will be appended.
674pub fn truncate_str<'a>(s: &'a str, width: usize, tail: &str) -> Cow<'a, str> {
675 #[cfg(feature = "ansi-parsing")]
676 {
677 use std::cmp::Ordering;
678 let mut iter = AnsiCodeIterator::new(s);
679 let mut length = 0;
680 let mut rv = None;
681
682 while let Some(item) = iter.next() {
683 match item {
684 (s, false) => {
685 if rv.is_none() {
686 if str_width(s) + length > width - str_width(tail) {
687 let ts = iter.current_slice();
688
689 let mut s_byte = 0;
690 let mut s_width = 0;
691 let rest_width = width - str_width(tail) - length;
692 for c in s.chars() {
693 s_byte += c.len_utf8();
694 s_width += char_width(c);
695 match s_width.cmp(&rest_width) {
696 Ordering::Equal => break,
697 Ordering::Greater => {
698 s_byte -= c.len_utf8();
699 break;
700 }
701 Ordering::Less => continue,
702 }
703 }
704
705 let idx = ts.len() - s.len() + s_byte;
706 let mut buf = ts[..idx].to_string();
707 buf.push_str(tail);
708 rv = Some(buf);
709 }
710 length += str_width(s);
711 }
712 }
713 (s, true) => {
714 if rv.is_some() {
715 rv.as_mut().unwrap().push_str(s);
716 }
717 }
718 }
719 }
720
721 if let Some(buf) = rv {
722 Cow::Owned(buf)
723 } else {
724 Cow::Borrowed(s)
725 }
726 }
727
728 #[cfg(not(feature = "ansi-parsing"))]
729 {
730 if s.len() <= width - tail.len() {
731 Cow::Borrowed(s)
732 } else {
733 Cow::Owned(format!(
734 "{}{}",
735 s.get(..width - tail.len()).unwrap_or_default(),
736 tail
737 ))
738 }
739 }
740}
741
742/// Pads a string to fill a certain number of characters.
743///
744/// This will honor ansi codes correctly and allows you to align a string
745/// on the left, right or centered. Additionally truncation can be enabled
746/// by setting `truncate` to a string that should be used as a truncation
747/// marker.
748pub fn pad_str<'a>(
749 s: &'a str,
750 width: usize,
751 align: Alignment,
752 truncate: Option<&str>,
753) -> Cow<'a, str> {
754 pad_str_with(s, width, align, truncate, ' ')
755}
756/// Pads a string with specific padding to fill a certain number of characters.
757///
758/// This will honor ansi codes correctly and allows you to align a string
759/// on the left, right or centered. Additionally truncation can be enabled
760/// by setting `truncate` to a string that should be used as a truncation
761/// marker.
762pub fn pad_str_with<'a>(
763 s: &'a str,
764 width: usize,
765 align: Alignment,
766 truncate: Option<&str>,
767 pad: char,
768) -> Cow<'a, str> {
769 let cols = measure_text_width(s);
770
771 if cols >= width {
772 return match truncate {
773 None => Cow::Borrowed(s),
774 Some(tail) => truncate_str(s, width, tail),
775 };
776 }
777
778 let diff = width - cols;
779
780 let (left_pad, right_pad) = match align {
781 Alignment::Left => (0, diff),
782 Alignment::Right => (diff, 0),
783 Alignment::Center => (diff / 2, diff - diff / 2),
784 };
785
786 let mut rv = String::new();
787 for _ in 0..left_pad {
788 rv.push(pad);
789 }
790 rv.push_str(s);
791 for _ in 0..right_pad {
792 rv.push(pad);
793 }
794 Cow::Owned(rv)
795}
796
797#[test]
798fn test_text_width() {
799 let s = style("foo")
800 .red()
801 .on_black()
802 .bold()
803 .force_styling(true)
804 .to_string();
805 assert_eq!(
806 measure_text_width(&s),
807 if cfg!(feature = "ansi-parsing") {
808 3
809 } else {
810 21
811 }
812 );
813}
814
815#[test]
816#[cfg(all(feature = "unicode-width", feature = "ansi-parsing"))]
817fn test_truncate_str() {
818 let s = format!("foo {}", style("bar").red().force_styling(true));
819 assert_eq!(
820 &truncate_str(&s, 5, ""),
821 &format!("foo {}", style("b").red().force_styling(true))
822 );
823 let s = format!("foo {}", style("bar").red().force_styling(true));
824 assert_eq!(
825 &truncate_str(&s, 5, "!"),
826 &format!("foo {}", style("!").red().force_styling(true))
827 );
828 let s = format!("foo {} baz", style("bar").red().force_styling(true));
829 assert_eq!(
830 &truncate_str(&s, 10, "..."),
831 &format!("foo {}...", style("bar").red().force_styling(true))
832 );
833 let s = format!("foo {}", style("バー").red().force_styling(true));
834 assert_eq!(
835 &truncate_str(&s, 5, ""),
836 &format!("foo {}", style("").red().force_styling(true))
837 );
838 let s = format!("foo {}", style("バー").red().force_styling(true));
839 assert_eq!(
840 &truncate_str(&s, 6, ""),
841 &format!("foo {}", style("バ").red().force_styling(true))
842 );
843}
844
845#[test]
846fn test_truncate_str_no_ansi() {
847 assert_eq!(&truncate_str("foo bar", 5, ""), "foo b");
848 assert_eq!(&truncate_str("foo bar", 5, "!"), "foo !");
849 assert_eq!(&truncate_str("foo bar baz", 10, "..."), "foo bar...");
850}
851
852#[test]
853fn test_pad_str() {
854 assert_eq!(pad_str("foo", 7, Alignment::Center, None), " foo ");
855 assert_eq!(pad_str("foo", 7, Alignment::Left, None), "foo ");
856 assert_eq!(pad_str("foo", 7, Alignment::Right, None), " foo");
857 assert_eq!(pad_str("foo", 3, Alignment::Left, None), "foo");
858 assert_eq!(pad_str("foobar", 3, Alignment::Left, None), "foobar");
859 assert_eq!(pad_str("foobar", 3, Alignment::Left, Some("")), "foo");
860 assert_eq!(
861 pad_str("foobarbaz", 6, Alignment::Left, Some("...")),
862 "foo..."
863 );
864}
865
866#[test]
867fn test_pad_str_with() {
868 assert_eq!(
869 pad_str_with("foo", 7, Alignment::Center, None, '#'),
870 "##foo##"
871 );
872 assert_eq!(
873 pad_str_with("foo", 7, Alignment::Left, None, '#'),
874 "foo####"
875 );
876 assert_eq!(
877 pad_str_with("foo", 7, Alignment::Right, None, '#'),
878 "####foo"
879 );
880 assert_eq!(pad_str_with("foo", 3, Alignment::Left, None, '#'), "foo");
881 assert_eq!(
882 pad_str_with("foobar", 3, Alignment::Left, None, '#'),
883 "foobar"
884 );
885 assert_eq!(
886 pad_str_with("foobar", 3, Alignment::Left, Some(""), '#'),
887 "foo"
888 );
889 assert_eq!(
890 pad_str_with("foobarbaz", 6, Alignment::Left, Some("..."), '#'),
891 "foo..."
892 );
893}