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