1use alloc::borrow::Cow;
2use core::{
3 fmt::{self, Debug, Formatter},
4 sync::atomic::{AtomicBool, Ordering},
5};
6use std::env;
7
8use once_cell::sync::Lazy;
9
10use crate::term::{wants_emoji, Term};
11
12#[cfg(feature = "ansi-parsing")]
13use crate::ansi::AnsiCodeIterator;
14
15fn default_colors_enabled(out: &Term) -> bool {
16 (out.features().colors_supported()
17 && &env::var("CLICOLOR").unwrap_or_else(|_| "1".into()) != "0")
18 || &env::var("CLICOLOR_FORCE").unwrap_or_else(|_| "0".into()) != "0"
19}
20
21fn default_true_colors_enabled(out: &Term) -> bool {
22 out.features().true_colors_supported()
23}
24
25static STDOUT_COLORS: Lazy<AtomicBool> =
26 Lazy::new(|| AtomicBool::new(default_colors_enabled(&Term::stdout())));
27static STDOUT_TRUE_COLORS: Lazy<AtomicBool> =
28 Lazy::new(|| AtomicBool::new(default_true_colors_enabled(&Term::stdout())));
29static STDERR_COLORS: Lazy<AtomicBool> =
30 Lazy::new(|| AtomicBool::new(default_colors_enabled(&Term::stderr())));
31static STDERR_TRUE_COLORS: Lazy<AtomicBool> =
32 Lazy::new(|| AtomicBool::new(default_true_colors_enabled(&Term::stderr())));
33
34#[inline]
42pub fn colors_enabled() -> bool {
43 STDOUT_COLORS.load(Ordering::Relaxed)
44}
45
46#[inline]
48pub fn true_colors_enabled() -> bool {
49 STDOUT_TRUE_COLORS.load(Ordering::Relaxed)
50}
51
52#[inline]
57pub fn set_colors_enabled(val: bool) {
58 STDOUT_COLORS.store(val, Ordering::Relaxed)
59}
60
61#[inline]
66pub fn set_true_colors_enabled(val: bool) {
67 STDOUT_TRUE_COLORS.store(val, Ordering::Relaxed)
68}
69
70#[inline]
78pub fn colors_enabled_stderr() -> bool {
79 STDERR_COLORS.load(Ordering::Relaxed)
80}
81
82#[inline]
84pub fn true_colors_enabled_stderr() -> bool {
85 STDERR_TRUE_COLORS.load(Ordering::Relaxed)
86}
87
88#[inline]
93pub fn set_colors_enabled_stderr(val: bool) {
94 STDERR_COLORS.store(val, Ordering::Relaxed)
95}
96
97#[inline]
102pub fn set_true_colors_enabled_stderr(val: bool) {
103 STDERR_TRUE_COLORS.store(val, Ordering::Relaxed)
104}
105
106pub fn measure_text_width(s: &str) -> usize {
108 #[cfg(feature = "ansi-parsing")]
109 {
110 AnsiCodeIterator::new(s)
111 .filter_map(|(s, is_ansi)| match is_ansi {
112 false => Some(str_width(s)),
113 true => None,
114 })
115 .sum()
116 }
117 #[cfg(not(feature = "ansi-parsing"))]
118 {
119 str_width(s)
120 }
121}
122
123#[derive(Copy, Clone, Debug, PartialEq, Eq)]
125pub enum Color {
126 Black,
127 Red,
128 Green,
129 Yellow,
130 Blue,
131 Magenta,
132 Cyan,
133 White,
134 Color256(u8),
135 TrueColor(u8, u8, u8),
136}
137
138impl Color {
139 #[inline]
140 fn ansi_num(self) -> usize {
141 match self {
142 Color::Black => 0,
143 Color::Red => 1,
144 Color::Green => 2,
145 Color::Yellow => 3,
146 Color::Blue => 4,
147 Color::Magenta => 5,
148 Color::Cyan => 6,
149 Color::White => 7,
150 Color::Color256(x) => x as usize,
151 Color::TrueColor(_, _, _) => panic!("RGB colors must be handled separately"),
152 }
153 }
154
155 #[inline]
156 fn is_color256(self) -> bool {
157 #[allow(clippy::match_like_matches_macro)]
158 match self {
159 Color::Color256(_) => true,
160 _ => false,
161 }
162 }
163}
164
165#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
167#[repr(u16)]
168pub enum Attribute {
169 Bold = 0,
172 Dim = 1,
173 Italic = 2,
174 Underlined = 3,
175 Blink = 4,
176 BlinkFast = 5,
177 Reverse = 6,
178 Hidden = 7,
179 StrikeThrough = 8,
180}
181
182impl Attribute {
183 const MAP: [Attribute; 9] = [
184 Attribute::Bold,
185 Attribute::Dim,
186 Attribute::Italic,
187 Attribute::Underlined,
188 Attribute::Blink,
189 Attribute::BlinkFast,
190 Attribute::Reverse,
191 Attribute::Hidden,
192 Attribute::StrikeThrough,
193 ];
194}
195
196#[derive(Clone, Copy, PartialEq, Eq)]
197struct Attributes(u16);
198
199impl Attributes {
200 #[inline]
201 const fn new() -> Self {
202 Self(0)
203 }
204
205 #[inline]
206 #[must_use]
207 const fn insert(mut self, attr: Attribute) -> Self {
208 let bit = attr as u16;
209 self.0 |= 1 << bit;
210 self
211 }
212
213 #[inline]
214 const fn bits(self) -> BitsIter {
215 BitsIter(self.0)
216 }
217
218 #[inline]
219 fn attrs(self) -> impl Iterator<Item = Attribute> {
220 self.bits().map(|bit| Attribute::MAP[bit as usize])
221 }
222
223 #[inline]
224 fn is_empty(self) -> bool {
225 self.0 == 0
226 }
227}
228
229impl fmt::Display for Attributes {
230 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231 for ansi in self.bits().map(|bit| bit + 1) {
232 write!(f, "\x1b[{ansi}m")?;
233 }
234 Ok(())
235 }
236}
237
238struct BitsIter(u16);
239
240impl Iterator for BitsIter {
241 type Item = u16;
242
243 fn next(&mut self) -> Option<Self::Item> {
244 if self.0 == 0 {
245 return None;
246 }
247 let bit = self.0.trailing_zeros();
248 self.0 ^= (1 << bit) as u16;
249 Some(bit as u16)
250 }
251}
252
253impl Debug for Attributes {
254 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
255 f.debug_set().entries(self.attrs()).finish()
256 }
257}
258
259#[derive(Copy, Clone, Debug, PartialEq, Eq)]
261pub enum Alignment {
262 Left,
263 Center,
264 Right,
265}
266
267#[derive(Clone, Debug, PartialEq, Eq)]
269pub struct Style {
270 fg: Option<Color>,
271 bg: Option<Color>,
272 fg_bright: bool,
273 bg_bright: bool,
274 attrs: Attributes,
275 force: Option<bool>,
276 for_stderr: bool,
277}
278
279impl Default for Style {
280 fn default() -> Self {
281 Self::new()
282 }
283}
284
285impl Style {
286 pub const fn new() -> Self {
288 Self {
289 fg: None,
290 bg: None,
291 fg_bright: false,
292 bg_bright: false,
293 attrs: Attributes::new(),
294 force: None,
295 for_stderr: false,
296 }
297 }
298
299 pub fn from_dotted_str(s: &str) -> Self {
307 let mut rv = Self::new();
308 for part in s.split('.') {
309 rv = match part {
310 "black" => rv.black(),
311 "red" => rv.red(),
312 "green" => rv.green(),
313 "yellow" => rv.yellow(),
314 "blue" => rv.blue(),
315 "magenta" => rv.magenta(),
316 "cyan" => rv.cyan(),
317 "white" => rv.white(),
318 "bright" => rv.bright(),
319 "on_black" => rv.on_black(),
320 "on_red" => rv.on_red(),
321 "on_green" => rv.on_green(),
322 "on_yellow" => rv.on_yellow(),
323 "on_blue" => rv.on_blue(),
324 "on_magenta" => rv.on_magenta(),
325 "on_cyan" => rv.on_cyan(),
326 "on_white" => rv.on_white(),
327 "on_bright" => rv.on_bright(),
328 "bold" => rv.bold(),
329 "dim" => rv.dim(),
330 "underlined" => rv.underlined(),
331 "blink" => rv.blink(),
332 "blink_fast" => rv.blink_fast(),
333 "reverse" => rv.reverse(),
334 "hidden" => rv.hidden(),
335 "strikethrough" => rv.strikethrough(),
336 on_true_color if on_true_color.starts_with("on_#") && on_true_color.len() == 10 => {
337 if let (Ok(r), Ok(g), Ok(b)) = (
338 u8::from_str_radix(&on_true_color[4..6], 16),
339 u8::from_str_radix(&on_true_color[6..8], 16),
340 u8::from_str_radix(&on_true_color[8..10], 16),
341 ) {
342 rv.on_true_color(r, g, b)
343 } else {
344 continue;
345 }
346 }
347 true_color if true_color.starts_with('#') && true_color.len() == 7 => {
348 if let (Ok(r), Ok(g), Ok(b)) = (
349 u8::from_str_radix(&true_color[1..3], 16),
350 u8::from_str_radix(&true_color[3..5], 16),
351 u8::from_str_radix(&true_color[5..7], 16),
352 ) {
353 rv.true_color(r, g, b)
354 } else {
355 continue;
356 }
357 }
358 on_c if on_c.starts_with("on_") => {
359 if let Ok(n) = on_c[3..].parse::<u8>() {
360 rv.on_color256(n)
361 } else {
362 continue;
363 }
364 }
365 c => {
366 if let Ok(n) = c.parse::<u8>() {
367 rv.color256(n)
368 } else {
369 continue;
370 }
371 }
372 };
373 }
374 rv
375 }
376
377 pub fn apply_to<D>(&self, val: D) -> StyledObject<D> {
379 StyledObject {
380 style: self.clone(),
381 val,
382 }
383 }
384
385 #[inline]
389 pub const fn force_styling(mut self, value: bool) -> Self {
390 self.force = Some(value);
391 self
392 }
393
394 #[inline]
396 pub const fn for_stderr(mut self) -> Self {
397 self.for_stderr = true;
398 self
399 }
400
401 #[inline]
405 pub const fn for_stdout(mut self) -> Self {
406 self.for_stderr = false;
407 self
408 }
409
410 #[inline]
412 pub const fn fg(mut self, color: Color) -> Self {
413 self.fg = Some(color);
414 self
415 }
416
417 #[inline]
419 pub const fn bg(mut self, color: Color) -> Self {
420 self.bg = Some(color);
421 self
422 }
423
424 #[inline]
426 pub const fn attr(mut self, attr: Attribute) -> Self {
427 self.attrs = self.attrs.insert(attr);
428 self
429 }
430
431 #[inline]
432 pub const fn black(self) -> Self {
433 self.fg(Color::Black)
434 }
435 #[inline]
436 pub const fn red(self) -> Self {
437 self.fg(Color::Red)
438 }
439 #[inline]
440 pub const fn green(self) -> Self {
441 self.fg(Color::Green)
442 }
443 #[inline]
444 pub const fn yellow(self) -> Self {
445 self.fg(Color::Yellow)
446 }
447 #[inline]
448 pub const fn blue(self) -> Self {
449 self.fg(Color::Blue)
450 }
451 #[inline]
452 pub const fn magenta(self) -> Self {
453 self.fg(Color::Magenta)
454 }
455 #[inline]
456 pub const fn cyan(self) -> Self {
457 self.fg(Color::Cyan)
458 }
459 #[inline]
460 pub const fn white(self) -> Self {
461 self.fg(Color::White)
462 }
463 #[inline]
464 pub const fn color256(self, color: u8) -> Self {
465 self.fg(Color::Color256(color))
466 }
467 #[inline]
468 pub const fn true_color(self, r: u8, g: u8, b: u8) -> Self {
469 self.fg(Color::TrueColor(r, g, b))
470 }
471
472 #[inline]
473 pub const fn bright(mut self) -> Self {
474 self.fg_bright = true;
475 self
476 }
477
478 #[inline]
479 pub const fn on_black(self) -> Self {
480 self.bg(Color::Black)
481 }
482 #[inline]
483 pub const fn on_red(self) -> Self {
484 self.bg(Color::Red)
485 }
486 #[inline]
487 pub const fn on_green(self) -> Self {
488 self.bg(Color::Green)
489 }
490 #[inline]
491 pub const fn on_yellow(self) -> Self {
492 self.bg(Color::Yellow)
493 }
494 #[inline]
495 pub const fn on_blue(self) -> Self {
496 self.bg(Color::Blue)
497 }
498 #[inline]
499 pub const fn on_magenta(self) -> Self {
500 self.bg(Color::Magenta)
501 }
502 #[inline]
503 pub const fn on_cyan(self) -> Self {
504 self.bg(Color::Cyan)
505 }
506 #[inline]
507 pub const fn on_white(self) -> Self {
508 self.bg(Color::White)
509 }
510 #[inline]
511 pub const fn on_color256(self, color: u8) -> Self {
512 self.bg(Color::Color256(color))
513 }
514 #[inline]
515 pub const fn on_true_color(self, r: u8, g: u8, b: u8) -> Self {
516 self.bg(Color::TrueColor(r, g, b))
517 }
518
519 #[inline]
520 pub const fn on_bright(mut self) -> Self {
521 self.bg_bright = true;
522 self
523 }
524
525 #[inline]
526 pub const fn bold(self) -> Self {
527 self.attr(Attribute::Bold)
528 }
529 #[inline]
530 pub const fn dim(self) -> Self {
531 self.attr(Attribute::Dim)
532 }
533 #[inline]
534 pub const fn italic(self) -> Self {
535 self.attr(Attribute::Italic)
536 }
537 #[inline]
538 pub const fn underlined(self) -> Self {
539 self.attr(Attribute::Underlined)
540 }
541 #[inline]
542 pub const fn blink(self) -> Self {
543 self.attr(Attribute::Blink)
544 }
545 #[inline]
546 pub const fn blink_fast(self) -> Self {
547 self.attr(Attribute::BlinkFast)
548 }
549 #[inline]
550 pub const fn reverse(self) -> Self {
551 self.attr(Attribute::Reverse)
552 }
553 #[inline]
554 pub const fn hidden(self) -> Self {
555 self.attr(Attribute::Hidden)
556 }
557 #[inline]
558 pub const fn strikethrough(self) -> Self {
559 self.attr(Attribute::StrikeThrough)
560 }
561}
562
563pub fn style<D>(val: D) -> StyledObject<D> {
580 Style::new().apply_to(val)
581}
582
583#[derive(Clone)]
585pub struct StyledObject<D> {
586 style: Style,
587 val: D,
588}
589
590impl<D> StyledObject<D> {
591 #[inline]
595 pub fn force_styling(mut self, value: bool) -> StyledObject<D> {
596 self.style = self.style.force_styling(value);
597 self
598 }
599
600 #[inline]
602 pub fn for_stderr(mut self) -> StyledObject<D> {
603 self.style = self.style.for_stderr();
604 self
605 }
606
607 #[inline]
611 pub const fn for_stdout(mut self) -> StyledObject<D> {
612 self.style = self.style.for_stdout();
613 self
614 }
615
616 #[inline]
618 pub const fn fg(mut self, color: Color) -> StyledObject<D> {
619 self.style = self.style.fg(color);
620 self
621 }
622
623 #[inline]
625 pub const fn bg(mut self, color: Color) -> StyledObject<D> {
626 self.style = self.style.bg(color);
627 self
628 }
629
630 #[inline]
632 pub const fn attr(mut self, attr: Attribute) -> StyledObject<D> {
633 self.style = self.style.attr(attr);
634 self
635 }
636
637 #[inline]
638 pub const fn black(self) -> StyledObject<D> {
639 self.fg(Color::Black)
640 }
641 #[inline]
642 pub const fn red(self) -> StyledObject<D> {
643 self.fg(Color::Red)
644 }
645 #[inline]
646 pub const fn green(self) -> StyledObject<D> {
647 self.fg(Color::Green)
648 }
649 #[inline]
650 pub const fn yellow(self) -> StyledObject<D> {
651 self.fg(Color::Yellow)
652 }
653 #[inline]
654 pub const fn blue(self) -> StyledObject<D> {
655 self.fg(Color::Blue)
656 }
657 #[inline]
658 pub const fn magenta(self) -> StyledObject<D> {
659 self.fg(Color::Magenta)
660 }
661 #[inline]
662 pub const fn cyan(self) -> StyledObject<D> {
663 self.fg(Color::Cyan)
664 }
665 #[inline]
666 pub const fn white(self) -> StyledObject<D> {
667 self.fg(Color::White)
668 }
669 #[inline]
670 pub const fn color256(self, color: u8) -> StyledObject<D> {
671 self.fg(Color::Color256(color))
672 }
673 #[inline]
674 pub const fn true_color(self, r: u8, g: u8, b: u8) -> StyledObject<D> {
675 self.fg(Color::TrueColor(r, g, b))
676 }
677
678 #[inline]
679 pub const fn bright(mut self) -> StyledObject<D> {
680 self.style = self.style.bright();
681 self
682 }
683
684 #[inline]
685 pub const fn on_black(self) -> StyledObject<D> {
686 self.bg(Color::Black)
687 }
688 #[inline]
689 pub const fn on_red(self) -> StyledObject<D> {
690 self.bg(Color::Red)
691 }
692 #[inline]
693 pub const fn on_green(self) -> StyledObject<D> {
694 self.bg(Color::Green)
695 }
696 #[inline]
697 pub const fn on_yellow(self) -> StyledObject<D> {
698 self.bg(Color::Yellow)
699 }
700 #[inline]
701 pub const fn on_blue(self) -> StyledObject<D> {
702 self.bg(Color::Blue)
703 }
704 #[inline]
705 pub const fn on_magenta(self) -> StyledObject<D> {
706 self.bg(Color::Magenta)
707 }
708 #[inline]
709 pub const fn on_cyan(self) -> StyledObject<D> {
710 self.bg(Color::Cyan)
711 }
712 #[inline]
713 pub const fn on_white(self) -> StyledObject<D> {
714 self.bg(Color::White)
715 }
716 #[inline]
717 pub const fn on_color256(self, color: u8) -> StyledObject<D> {
718 self.bg(Color::Color256(color))
719 }
720 #[inline]
721 pub const fn on_true_color(self, r: u8, g: u8, b: u8) -> StyledObject<D> {
722 self.bg(Color::TrueColor(r, g, b))
723 }
724
725 #[inline]
726 pub const fn on_bright(mut self) -> StyledObject<D> {
727 self.style = self.style.on_bright();
728 self
729 }
730
731 #[inline]
732 pub const fn bold(self) -> StyledObject<D> {
733 self.attr(Attribute::Bold)
734 }
735 #[inline]
736 pub const fn dim(self) -> StyledObject<D> {
737 self.attr(Attribute::Dim)
738 }
739 #[inline]
740 pub const fn italic(self) -> StyledObject<D> {
741 self.attr(Attribute::Italic)
742 }
743 #[inline]
744 pub const fn underlined(self) -> StyledObject<D> {
745 self.attr(Attribute::Underlined)
746 }
747 #[inline]
748 pub const fn blink(self) -> StyledObject<D> {
749 self.attr(Attribute::Blink)
750 }
751 #[inline]
752 pub const fn blink_fast(self) -> StyledObject<D> {
753 self.attr(Attribute::BlinkFast)
754 }
755 #[inline]
756 pub const fn reverse(self) -> StyledObject<D> {
757 self.attr(Attribute::Reverse)
758 }
759 #[inline]
760 pub const fn hidden(self) -> StyledObject<D> {
761 self.attr(Attribute::Hidden)
762 }
763 #[inline]
764 pub const fn strikethrough(self) -> StyledObject<D> {
765 self.attr(Attribute::StrikeThrough)
766 }
767}
768
769macro_rules! impl_fmt {
770 ($name:ident) => {
771 impl<D: fmt::$name> fmt::$name for StyledObject<D> {
772 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
773 let mut reset = false;
774 if self
775 .style
776 .force
777 .unwrap_or_else(|| match self.style.for_stderr {
778 true => colors_enabled_stderr(),
779 false => colors_enabled(),
780 })
781 {
782 if let Some(fg) = self.style.fg {
783 if let Color::TrueColor(r, g, b) = fg {
784 write!(f, "\x1b[38;2;{};{};{}m", r, g, b)?;
785 } else if fg.is_color256() {
786 write!(f, "\x1b[38;5;{}m", fg.ansi_num())?;
787 } else if self.style.fg_bright {
788 write!(f, "\x1b[38;5;{}m", fg.ansi_num() + 8)?;
789 } else {
790 write!(f, "\x1b[{}m", fg.ansi_num() + 30)?;
791 }
792 reset = true;
793 }
794 if let Some(bg) = self.style.bg {
795 if let Color::TrueColor(r, g, b) = bg {
796 write!(f, "\x1b[48;2;{};{};{}m", r, g, b)?;
797 } else if bg.is_color256() {
798 write!(f, "\x1b[48;5;{}m", bg.ansi_num())?;
799 } else if self.style.bg_bright {
800 write!(f, "\x1b[48;5;{}m", bg.ansi_num() + 8)?;
801 } else {
802 write!(f, "\x1b[{}m", bg.ansi_num() + 40)?;
803 }
804 reset = true;
805 }
806 if !self.style.attrs.is_empty() {
807 write!(f, "{}", self.style.attrs)?;
808 reset = true;
809 }
810 }
811 fmt::$name::fmt(&self.val, f)?;
812 if reset {
813 write!(f, "\x1b[0m")?;
814 }
815 Ok(())
816 }
817 }
818 };
819}
820
821impl_fmt!(Binary);
822impl_fmt!(Debug);
823impl_fmt!(Display);
824impl_fmt!(LowerExp);
825impl_fmt!(LowerHex);
826impl_fmt!(Octal);
827impl_fmt!(Pointer);
828impl_fmt!(UpperExp);
829impl_fmt!(UpperHex);
830
831#[derive(Copy, Clone)]
844pub struct Emoji<'a, 'b>(pub &'a str, pub &'b str);
845
846impl<'a, 'b> Emoji<'a, 'b> {
847 pub fn new(emoji: &'a str, fallback: &'b str) -> Emoji<'a, 'b> {
848 Emoji(emoji, fallback)
849 }
850}
851
852impl fmt::Display for Emoji<'_, '_> {
853 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
854 if wants_emoji() {
855 write!(f, "{}", self.0)
856 } else {
857 write!(f, "{}", self.1)
858 }
859 }
860}
861
862fn str_width(s: &str) -> usize {
863 #[cfg(feature = "unicode-width")]
864 {
865 use unicode_width::UnicodeWidthStr;
866 s.width()
867 }
868 #[cfg(not(feature = "unicode-width"))]
869 {
870 s.chars().count()
871 }
872}
873
874#[cfg(feature = "ansi-parsing")]
875pub(crate) fn char_width(c: char) -> usize {
876 #[cfg(feature = "unicode-width")]
877 {
878 use unicode_width::UnicodeWidthChar;
879 c.width().unwrap_or(0)
880 }
881 #[cfg(not(feature = "unicode-width"))]
882 {
883 let _c = c;
884 1
885 }
886}
887
888#[cfg(not(feature = "ansi-parsing"))]
889pub(crate) fn char_width(_c: char) -> usize {
890 1
891}
892
893pub fn truncate_str<'a>(s: &'a str, width: usize, tail: &str) -> Cow<'a, str> {
900 if measure_text_width(s) <= width {
901 return Cow::Borrowed(s);
902 }
903
904 #[cfg(feature = "ansi-parsing")]
905 {
906 use core::cmp::Ordering;
907 let mut iter = AnsiCodeIterator::new(s);
908 let mut length = 0;
909 let mut rv = None;
910
911 while let Some(item) = iter.next() {
912 match item {
913 (s, false) => {
914 if rv.is_none() {
915 if str_width(s) + length > width.saturating_sub(str_width(tail)) {
916 let ts = iter.current_slice();
917
918 let mut s_byte = 0;
919 let mut s_width = 0;
920 let rest_width =
921 width.saturating_sub(str_width(tail)).saturating_sub(length);
922 for c in s.chars() {
923 s_byte += c.len_utf8();
924 s_width += char_width(c);
925 match s_width.cmp(&rest_width) {
926 Ordering::Equal => break,
927 Ordering::Greater => {
928 s_byte -= c.len_utf8();
929 break;
930 }
931 Ordering::Less => continue,
932 }
933 }
934
935 let idx = ts.len() - s.len() + s_byte;
936 let mut buf = ts[..idx].to_string();
937 buf.push_str(tail);
938 rv = Some(buf);
939 }
940 length += str_width(s);
941 }
942 }
943 (s, true) => {
944 if let Some(ref mut rv) = rv {
945 rv.push_str(s);
946 }
947 }
948 }
949 }
950
951 if let Some(buf) = rv {
952 Cow::Owned(buf)
953 } else {
954 Cow::Borrowed(s)
955 }
956 }
957
958 #[cfg(not(feature = "ansi-parsing"))]
959 {
960 Cow::Owned(format!(
961 "{}{}",
962 &s[..width.saturating_sub(tail.len())],
963 tail
964 ))
965 }
966}
967
968pub fn pad_str<'a>(
975 s: &'a str,
976 width: usize,
977 align: Alignment,
978 truncate: Option<&str>,
979) -> Cow<'a, str> {
980 pad_str_with(s, width, align, truncate, ' ')
981}
982pub fn pad_str_with<'a>(
989 s: &'a str,
990 width: usize,
991 align: Alignment,
992 truncate: Option<&str>,
993 pad: char,
994) -> Cow<'a, str> {
995 let cols = measure_text_width(s);
996
997 if cols >= width {
998 return match truncate {
999 None => Cow::Borrowed(s),
1000 Some(tail) => truncate_str(s, width, tail),
1001 };
1002 }
1003
1004 let diff = width - cols;
1005
1006 let (left_pad, right_pad) = match align {
1007 Alignment::Left => (0, diff),
1008 Alignment::Right => (diff, 0),
1009 Alignment::Center => (diff / 2, diff - diff / 2),
1010 };
1011
1012 let mut rv = String::new();
1013 for _ in 0..left_pad {
1014 rv.push(pad);
1015 }
1016 rv.push_str(s);
1017 for _ in 0..right_pad {
1018 rv.push(pad);
1019 }
1020 Cow::Owned(rv)
1021}
1022
1023#[test]
1024fn test_text_width() {
1025 let s = style("foo")
1026 .red()
1027 .on_black()
1028 .bold()
1029 .force_styling(true)
1030 .to_string();
1031
1032 assert_eq!(
1033 measure_text_width(&s),
1034 if cfg!(feature = "ansi-parsing") {
1035 3
1036 } else {
1037 21
1038 }
1039 );
1040
1041 let s = style("🐶 <3").red().force_styling(true).to_string();
1042
1043 assert_eq!(
1044 measure_text_width(&s),
1045 match (
1046 cfg!(feature = "ansi-parsing"),
1047 cfg!(feature = "unicode-width")
1048 ) {
1049 (true, true) => 5, (true, false) => 4, (false, true) => 14, (false, false) => 13, }
1054 );
1055}
1056
1057#[test]
1058#[cfg(all(feature = "unicode-width", feature = "ansi-parsing"))]
1059fn test_truncate_str() {
1060 let s = format!("foo {}", style("bar").red().force_styling(true));
1061 assert_eq!(
1062 &truncate_str(&s, 5, ""),
1063 &format!("foo {}", style("b").red().force_styling(true))
1064 );
1065 let s = format!("foo {}", style("bar").red().force_styling(true));
1066 assert_eq!(
1067 &truncate_str(&s, 5, "!"),
1068 &format!("foo {}", style("!").red().force_styling(true))
1069 );
1070 let s = format!("foo {} baz", style("bar").red().force_styling(true));
1071 assert_eq!(
1072 &truncate_str(&s, 10, "..."),
1073 &format!("foo {}...", style("bar").red().force_styling(true))
1074 );
1075 let s = format!("foo {}", style("バー").red().force_styling(true));
1076 assert_eq!(
1077 &truncate_str(&s, 5, ""),
1078 &format!("foo {}", style("").red().force_styling(true))
1079 );
1080 let s = format!("foo {}", style("バー").red().force_styling(true));
1081 assert_eq!(
1082 &truncate_str(&s, 6, ""),
1083 &format!("foo {}", style("バ").red().force_styling(true))
1084 );
1085 let s = format!("foo {}", style("バー").red().force_styling(true));
1086 assert_eq!(
1087 &truncate_str(&s, 2, "!!!"),
1088 &format!("!!!{}", style("").red().force_styling(true))
1089 );
1090}
1091
1092#[test]
1093fn test_truncate_str_no_ansi() {
1094 assert_eq!(&truncate_str("foo bar", 7, "!"), "foo bar");
1095 assert_eq!(&truncate_str("foo bar", 5, ""), "foo b");
1096 assert_eq!(&truncate_str("foo bar", 5, "!"), "foo !");
1097 assert_eq!(&truncate_str("foo bar baz", 10, "..."), "foo bar...");
1098 assert_eq!(&truncate_str("foo bar", 0, ""), "");
1099 assert_eq!(&truncate_str("foo bar", 0, "!"), "!");
1100 assert_eq!(&truncate_str("foo bar", 2, "!!!"), "!!!");
1101 assert_eq!(&truncate_str("ab", 2, "!!!"), "ab");
1102}
1103
1104#[test]
1105fn test_pad_str() {
1106 assert_eq!(pad_str("foo", 7, Alignment::Center, None), " foo ");
1107 assert_eq!(pad_str("foo", 7, Alignment::Left, None), "foo ");
1108 assert_eq!(pad_str("foo", 7, Alignment::Right, None), " foo");
1109 assert_eq!(pad_str("foo", 3, Alignment::Left, None), "foo");
1110 assert_eq!(pad_str("foobar", 3, Alignment::Left, None), "foobar");
1111 assert_eq!(pad_str("foobar", 3, Alignment::Left, Some("")), "foo");
1112 assert_eq!(
1113 pad_str("foobarbaz", 6, Alignment::Left, Some("...")),
1114 "foo..."
1115 );
1116}
1117
1118#[test]
1119fn test_pad_str_with() {
1120 assert_eq!(
1121 pad_str_with("foo", 7, Alignment::Center, None, '#'),
1122 "##foo##"
1123 );
1124 assert_eq!(
1125 pad_str_with("foo", 7, Alignment::Left, None, '#'),
1126 "foo####"
1127 );
1128 assert_eq!(
1129 pad_str_with("foo", 7, Alignment::Right, None, '#'),
1130 "####foo"
1131 );
1132 assert_eq!(pad_str_with("foo", 3, Alignment::Left, None, '#'), "foo");
1133 assert_eq!(
1134 pad_str_with("foobar", 3, Alignment::Left, None, '#'),
1135 "foobar"
1136 );
1137 assert_eq!(
1138 pad_str_with("foobar", 3, Alignment::Left, Some(""), '#'),
1139 "foo"
1140 );
1141 assert_eq!(
1142 pad_str_with("foobarbaz", 6, Alignment::Left, Some("..."), '#'),
1143 "foo..."
1144 );
1145}
1146
1147#[test]
1148fn test_attributes_single() {
1149 for attr in Attribute::MAP {
1150 let attrs = Attributes::new().insert(attr);
1151 assert_eq!(attrs.bits().collect::<Vec<_>>(), [attr as u16]);
1152 assert_eq!(attrs.attrs().collect::<Vec<_>>(), [attr]);
1153 assert_eq!(format!("{attrs:?}"), format!("{{{:?}}}", attr));
1154 }
1155}
1156
1157#[test]
1158fn test_attributes_many() {
1159 let tests: [&[Attribute]; 3] = [
1160 &[
1161 Attribute::Bold,
1162 Attribute::Underlined,
1163 Attribute::BlinkFast,
1164 Attribute::Hidden,
1165 ],
1166 &[
1167 Attribute::Dim,
1168 Attribute::Italic,
1169 Attribute::Blink,
1170 Attribute::Reverse,
1171 Attribute::StrikeThrough,
1172 ],
1173 &Attribute::MAP,
1174 ];
1175 for test_attrs in tests {
1176 let mut attrs = Attributes::new();
1177 for attr in test_attrs {
1178 attrs = attrs.insert(*attr);
1179 }
1180 assert_eq!(
1181 attrs.bits().collect::<Vec<_>>(),
1182 test_attrs
1183 .iter()
1184 .map(|attr| *attr as u16)
1185 .collect::<Vec<_>>()
1186 );
1187 assert_eq!(&attrs.attrs().collect::<Vec<_>>(), test_attrs);
1188 }
1189}