1use std::borrow::Cow;
2use std::collections::BTreeSet;
3use std::env;
4use std::fmt;
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use once_cell::sync::Lazy;
8
9use crate::term::{wants_emoji, Term};
10
11#[cfg(feature = "ansi-parsing")]
12use crate::ansi::{strip_ansi_codes, AnsiCodeIterator};
13
14#[cfg(not(feature = "ansi-parsing"))]
15fn strip_ansi_codes(s: &str) -> &str {
16 s
17}
18
19fn default_colors_enabled(out: &Term) -> bool {
20 (out.features().colors_supported()
21 && &env::var("CLICOLOR").unwrap_or_else(|_| "1".into()) != "0")
22 || &env::var("CLICOLOR_FORCE").unwrap_or_else(|_| "0".into()) != "0"
23}
24
25static STDOUT_COLORS: Lazy<AtomicBool> =
26 Lazy::new(|| AtomicBool::new(default_colors_enabled(&Term::stdout())));
27static STDERR_COLORS: Lazy<AtomicBool> =
28 Lazy::new(|| AtomicBool::new(default_colors_enabled(&Term::stderr())));
29
30#[inline]
38pub fn colors_enabled() -> bool {
39 STDOUT_COLORS.load(Ordering::Relaxed)
40}
41
42#[inline]
47pub fn set_colors_enabled(val: bool) {
48 STDOUT_COLORS.store(val, Ordering::Relaxed)
49}
50
51#[inline]
59pub fn colors_enabled_stderr() -> bool {
60 STDERR_COLORS.load(Ordering::Relaxed)
61}
62
63#[inline]
68pub fn set_colors_enabled_stderr(val: bool) {
69 STDERR_COLORS.store(val, Ordering::Relaxed)
70}
71
72pub fn measure_text_width(s: &str) -> usize {
74 str_width(&strip_ansi_codes(s))
75}
76
77#[derive(Copy, Clone, Debug, PartialEq, Eq)]
79pub enum Color {
80 Black,
81 Red,
82 Green,
83 Yellow,
84 Blue,
85 Magenta,
86 Cyan,
87 White,
88 Color256(u8),
89}
90
91impl Color {
92 #[inline]
93 fn ansi_num(self) -> usize {
94 match self {
95 Color::Black => 0,
96 Color::Red => 1,
97 Color::Green => 2,
98 Color::Yellow => 3,
99 Color::Blue => 4,
100 Color::Magenta => 5,
101 Color::Cyan => 6,
102 Color::White => 7,
103 Color::Color256(x) => x as usize,
104 }
105 }
106
107 #[inline]
108 fn is_color256(self) -> bool {
109 #[allow(clippy::match_like_matches_macro)]
110 match self {
111 Color::Color256(_) => true,
112 _ => false,
113 }
114 }
115}
116
117#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
119pub enum Attribute {
120 Bold,
121 Dim,
122 Italic,
123 Underlined,
124 Blink,
125 BlinkFast,
126 Reverse,
127 Hidden,
128 StrikeThrough,
129}
130
131impl 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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
150pub enum Alignment {
151 Left,
152 Center,
153 Right,
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
158pub 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
168impl Default for Style {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174impl Style {
175 pub const fn new() -> Self {
177 Self {
178 fg: None,
179 bg: None,
180 fg_bright: false,
181 bg_bright: false,
182 attrs: BTreeSet::new(),
183 force: None,
184 for_stderr: false,
185 }
186 }
187
188 pub fn from_dotted_str(s: &str) -> Self {
196 let mut rv = Self::new();
197 for part in s.split('.') {
198 rv = match part {
199 "black" => rv.black(),
200 "red" => rv.red(),
201 "green" => rv.green(),
202 "yellow" => rv.yellow(),
203 "blue" => rv.blue(),
204 "magenta" => rv.magenta(),
205 "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 rv
242 }
243
244 pub fn apply_to<D>(&self, val: D) -> StyledObject<D> {
246 StyledObject {
247 style: self.clone(),
248 val,
249 }
250 }
251
252 #[inline]
256 pub const fn force_styling(mut self, value: bool) -> Self {
257 self.force = Some(value);
258 self
259 }
260
261 #[inline]
263 pub const fn for_stderr(mut self) -> Self {
264 self.for_stderr = true;
265 self
266 }
267
268 #[inline]
272 pub const fn for_stdout(mut self) -> Self {
273 self.for_stderr = false;
274 self
275 }
276
277 #[inline]
279 pub const fn fg(mut self, color: Color) -> Self {
280 self.fg = Some(color);
281 self
282 }
283
284 #[inline]
286 pub const fn bg(mut self, color: Color) -> Self {
287 self.bg = Some(color);
288 self
289 }
290
291 #[inline]
293 pub fn attr(mut self, attr: Attribute) -> Self {
294 self.attrs.insert(attr);
295 self
296 }
297
298 #[inline]
299 pub const fn black(self) -> Self {
300 self.fg(Color::Black)
301 }
302 #[inline]
303 pub const fn red(self) -> Self {
304 self.fg(Color::Red)
305 }
306 #[inline]
307 pub const fn green(self) -> Self {
308 self.fg(Color::Green)
309 }
310 #[inline]
311 pub const fn yellow(self) -> Self {
312 self.fg(Color::Yellow)
313 }
314 #[inline]
315 pub const fn blue(self) -> Self {
316 self.fg(Color::Blue)
317 }
318 #[inline]
319 pub const fn magenta(self) -> Self {
320 self.fg(Color::Magenta)
321 }
322 #[inline]
323 pub const fn cyan(self) -> Self {
324 self.fg(Color::Cyan)
325 }
326 #[inline]
327 pub const fn white(self) -> Self {
328 self.fg(Color::White)
329 }
330 #[inline]
331 pub const fn color256(self, color: u8) -> Self {
332 self.fg(Color::Color256(color))
333 }
334
335 #[inline]
336 pub const fn bright(mut self) -> Self {
337 self.fg_bright = true;
338 self
339 }
340
341 #[inline]
342 pub const fn on_black(self) -> Self {
343 self.bg(Color::Black)
344 }
345 #[inline]
346 pub const fn on_red(self) -> Self {
347 self.bg(Color::Red)
348 }
349 #[inline]
350 pub const fn on_green(self) -> Self {
351 self.bg(Color::Green)
352 }
353 #[inline]
354 pub const fn on_yellow(self) -> Self {
355 self.bg(Color::Yellow)
356 }
357 #[inline]
358 pub const fn on_blue(self) -> Self {
359 self.bg(Color::Blue)
360 }
361 #[inline]
362 pub const fn on_magenta(self) -> Self {
363 self.bg(Color::Magenta)
364 }
365 #[inline]
366 pub const fn on_cyan(self) -> Self {
367 self.bg(Color::Cyan)
368 }
369 #[inline]
370 pub const fn on_white(self) -> Self {
371 self.bg(Color::White)
372 }
373 #[inline]
374 pub const fn on_color256(self, color: u8) -> Self {
375 self.bg(Color::Color256(color))
376 }
377
378 #[inline]
379 pub const fn on_bright(mut self) -> Self {
380 self.bg_bright = true;
381 self
382 }
383
384 #[inline]
385 pub fn bold(self) -> Self {
386 self.attr(Attribute::Bold)
387 }
388 #[inline]
389 pub fn dim(self) -> Self {
390 self.attr(Attribute::Dim)
391 }
392 #[inline]
393 pub fn italic(self) -> Self {
394 self.attr(Attribute::Italic)
395 }
396 #[inline]
397 pub fn underlined(self) -> Self {
398 self.attr(Attribute::Underlined)
399 }
400 #[inline]
401 pub fn blink(self) -> Self {
402 self.attr(Attribute::Blink)
403 }
404 #[inline]
405 pub fn blink_fast(self) -> Self {
406 self.attr(Attribute::BlinkFast)
407 }
408 #[inline]
409 pub fn reverse(self) -> Self {
410 self.attr(Attribute::Reverse)
411 }
412 #[inline]
413 pub fn hidden(self) -> Self {
414 self.attr(Attribute::Hidden)
415 }
416 #[inline]
417 pub fn strikethrough(self) -> Self {
418 self.attr(Attribute::StrikeThrough)
419 }
420}
421
422pub fn style<D>(val: D) -> StyledObject<D> {
439 Style::new().apply_to(val)
440}
441
442#[derive(Clone)]
444pub struct StyledObject<D> {
445 style: Style,
446 val: D,
447}
448
449impl<D> StyledObject<D> {
450 #[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 #[inline]
461 pub fn for_stderr(mut self) -> StyledObject<D> {
462 self.style = self.style.for_stderr();
463 self
464 }
465
466 #[inline]
470 pub fn for_stdout(mut self) -> StyledObject<D> {
471 self.style = self.style.for_stdout();
472 self
473 }
474
475 #[inline]
477 pub fn fg(mut self, color: Color) -> StyledObject<D> {
478 self.style = self.style.fg(color);
479 self
480 }
481
482 #[inline]
484 pub fn bg(mut self, color: Color) -> StyledObject<D> {
485 self.style = self.style.bg(color);
486 self
487 }
488
489 #[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
620macro_rules! impl_fmt {
621 ($name:ident) => {
622 impl<D: fmt::$name> fmt::$name for StyledObject<D> {
623 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
624 let mut reset = false;
625 if self
626 .style
627 .force
628 .unwrap_or_else(|| match self.style.for_stderr {
629 true => colors_enabled_stderr(),
630 false => colors_enabled(),
631 })
632 {
633 if let Some(fg) = self.style.fg {
634 if fg.is_color256() {
635 write!(f, "\x1b[38;5;{}m", fg.ansi_num())?;
636 } else if self.style.fg_bright {
637 write!(f, "\x1b[38;5;{}m", fg.ansi_num() + 8)?;
638 } else {
639 write!(f, "\x1b[{}m", fg.ansi_num() + 30)?;
640 }
641 reset = true;
642 }
643 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 }
653 for attr in &self.style.attrs {
654 write!(f, "\x1b[{}m", attr.ansi_num())?;
655 reset = true;
656 }
657 }
658 fmt::$name::fmt(&self.val, f)?;
659 if reset {
660 write!(f, "\x1b[0m")?;
661 }
662 Ok(())
663 }
664 }
665 };
666}
667
668impl_fmt!(Binary);
669impl_fmt!(Debug);
670impl_fmt!(Display);
671impl_fmt!(LowerExp);
672impl_fmt!(LowerHex);
673impl_fmt!(Octal);
674impl_fmt!(Pointer);
675impl_fmt!(UpperExp);
676impl_fmt!(UpperHex);
677
678#[derive(Copy, Clone)]
691pub struct Emoji<'a, 'b>(pub &'a str, pub &'b str);
692
693impl<'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
699impl fmt::Display for Emoji<'_, '_> {
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
709fn str_width(s: &str) -> usize {
710 #[cfg(feature = "unicode-width")]
711 {
712 use unicode_width::UnicodeWidthStr;
713 s.width()
714 }
715 #[cfg(not(feature = "unicode-width"))]
716 {
717 s.chars().count()
718 }
719}
720
721#[cfg(feature = "ansi-parsing")]
722pub(crate) 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#[cfg(not(feature = "ansi-parsing"))]
736pub(crate) fn char_width(_c: char) -> usize {
737 1
738}
739
740pub fn truncate_str<'a>(s: &'a str, width: usize, tail: &str) -> Cow<'a, str> {
747 #[cfg(feature = "ansi-parsing")]
748 {
749 use std::cmp::Ordering;
750 let mut iter = AnsiCodeIterator::new(s);
751 let mut length = 0;
752 let mut rv = None;
753
754 while let Some(item) = iter.next() {
755 match item {
756 (s, false) => {
757 if rv.is_none() {
758 if str_width(s) + length > width - str_width(tail) {
759 let ts = iter.current_slice();
760
761 let mut s_byte = 0;
762 let mut s_width = 0;
763 let rest_width = width - str_width(tail) - length;
764 for c in s.chars() {
765 s_byte += c.len_utf8();
766 s_width += char_width(c);
767 match s_width.cmp(&rest_width) {
768 Ordering::Equal => break,
769 Ordering::Greater => {
770 s_byte -= c.len_utf8();
771 break;
772 }
773 Ordering::Less => continue,
774 }
775 }
776
777 let idx = ts.len() - s.len() + s_byte;
778 let mut buf = ts[..idx].to_string();
779 buf.push_str(tail);
780 rv = Some(buf);
781 }
782 length += str_width(s);
783 }
784 }
785 (s, true) => {
786 if let Some(ref mut rv) = rv {
787 rv.push_str(s);
788 }
789 }
790 }
791 }
792
793 if let Some(buf) = rv {
794 Cow::Owned(buf)
795 } else {
796 Cow::Borrowed(s)
797 }
798 }
799
800 #[cfg(not(feature = "ansi-parsing"))]
801 {
802 if s.len() <= width - tail.len() {
803 Cow::Borrowed(s)
804 } else {
805 Cow::Owned(format!(
806 "{}{}",
807 s.get(..width - tail.len()).unwrap_or_default(),
808 tail
809 ))
810 }
811 }
812}
813
814pub fn pad_str<'a>(
821 s: &'a str,
822 width: usize,
823 align: Alignment,
824 truncate: Option<&str>,
825) -> Cow<'a, str> {
826 pad_str_with(s, width, align, truncate, ' ')
827}
828pub fn pad_str_with<'a>(
835 s: &'a str,
836 width: usize,
837 align: Alignment,
838 truncate: Option<&str>,
839 pad: char,
840) -> Cow<'a, str> {
841 let cols = measure_text_width(s);
842
843 if cols >= width {
844 return match truncate {
845 None => Cow::Borrowed(s),
846 Some(tail) => truncate_str(s, width, tail),
847 };
848 }
849
850 let diff = width - cols;
851
852 let (left_pad, right_pad) = match align {
853 Alignment::Left => (0, diff),
854 Alignment::Right => (diff, 0),
855 Alignment::Center => (diff / 2, diff - diff / 2),
856 };
857
858 let mut rv = String::new();
859 for _ in 0..left_pad {
860 rv.push(pad);
861 }
862 rv.push_str(s);
863 for _ in 0..right_pad {
864 rv.push(pad);
865 }
866 Cow::Owned(rv)
867}
868
869#[test]
870fn test_text_width() {
871 let s = style("foo")
872 .red()
873 .on_black()
874 .bold()
875 .force_styling(true)
876 .to_string();
877 assert_eq!(
878 measure_text_width(&s),
879 if cfg!(feature = "ansi-parsing") {
880 3
881 } else if cfg!(feature = "unicode-width") {
882 17
883 } else {
884 21
885 }
886 );
887}
888
889#[test]
890#[cfg(all(feature = "unicode-width", feature = "ansi-parsing"))]
891fn test_truncate_str() {
892 let s = format!("foo {}", style("bar").red().force_styling(true));
893 assert_eq!(
894 &truncate_str(&s, 5, ""),
895 &format!("foo {}", style("b").red().force_styling(true))
896 );
897 let s = format!("foo {}", style("bar").red().force_styling(true));
898 assert_eq!(
899 &truncate_str(&s, 5, "!"),
900 &format!("foo {}", style("!").red().force_styling(true))
901 );
902 let s = format!("foo {} baz", style("bar").red().force_styling(true));
903 assert_eq!(
904 &truncate_str(&s, 10, "..."),
905 &format!("foo {}...", style("bar").red().force_styling(true))
906 );
907 let s = format!("foo {}", style("バー").red().force_styling(true));
908 assert_eq!(
909 &truncate_str(&s, 5, ""),
910 &format!("foo {}", style("").red().force_styling(true))
911 );
912 let s = format!("foo {}", style("バー").red().force_styling(true));
913 assert_eq!(
914 &truncate_str(&s, 6, ""),
915 &format!("foo {}", style("バ").red().force_styling(true))
916 );
917}
918
919#[test]
920fn test_truncate_str_no_ansi() {
921 assert_eq!(&truncate_str("foo bar", 5, ""), "foo b");
922 assert_eq!(&truncate_str("foo bar", 5, "!"), "foo !");
923 assert_eq!(&truncate_str("foo bar baz", 10, "..."), "foo bar...");
924}
925
926#[test]
927fn test_pad_str() {
928 assert_eq!(pad_str("foo", 7, Alignment::Center, None), " foo ");
929 assert_eq!(pad_str("foo", 7, Alignment::Left, None), "foo ");
930 assert_eq!(pad_str("foo", 7, Alignment::Right, None), " foo");
931 assert_eq!(pad_str("foo", 3, Alignment::Left, None), "foo");
932 assert_eq!(pad_str("foobar", 3, Alignment::Left, None), "foobar");
933 assert_eq!(pad_str("foobar", 3, Alignment::Left, Some("")), "foo");
934 assert_eq!(
935 pad_str("foobarbaz", 6, Alignment::Left, Some("...")),
936 "foo..."
937 );
938}
939
940#[test]
941fn test_pad_str_with() {
942 assert_eq!(
943 pad_str_with("foo", 7, Alignment::Center, None, '#'),
944 "##foo##"
945 );
946 assert_eq!(
947 pad_str_with("foo", 7, Alignment::Left, None, '#'),
948 "foo####"
949 );
950 assert_eq!(
951 pad_str_with("foo", 7, Alignment::Right, None, '#'),
952 "####foo"
953 );
954 assert_eq!(pad_str_with("foo", 3, Alignment::Left, None, '#'), "foo");
955 assert_eq!(
956 pad_str_with("foobar", 3, Alignment::Left, None, '#'),
957 "foobar"
958 );
959 assert_eq!(
960 pad_str_with("foobar", 3, Alignment::Left, Some(""), '#'),
961 "foo"
962 );
963 assert_eq!(
964 pad_str_with("foobarbaz", 6, Alignment::Left, Some("..."), '#'),
965 "foo..."
966 );
967}