1#![allow(clippy::uninlined_format_args)]
2
3#![warn(missing_docs)]
47
48use std::borrow::Cow;
49use std::fmt::Write;
50use std::ops::{Bound, RangeBounds};
51
52use ansitok::{parse_ansi, AnsiColor, AnsiIterator, ElementKind};
53
54pub trait AnsiStr {
57 fn ansi_get<I>(&self, i: I) -> Option<Cow<'_, str>>
102 where
103 I: RangeBounds<usize>;
104
105 fn ansi_cut<I>(&self, i: I) -> Cow<'_, str>
140 where
141 I: RangeBounds<usize>;
142
143 fn ansi_is_char_boundary(&self, index: usize) -> bool;
168
169 fn ansi_find(&self, pat: &str) -> Option<usize>;
187
188 fn ansi_strip_prefix(&self, prefix: &str) -> Option<Cow<'_, str>>;
213
214 fn ansi_strip_suffix(&self, pat: &str) -> Option<Cow<'_, str>>;
233
234 fn ansi_split<'a>(&'a self, pat: &'a str) -> AnsiSplit<'a>;
271
272 fn ansi_split_at(&self, mid: usize) -> (Cow<'_, str>, Cow<'_, str>);
309
310 fn ansi_starts_with(&self, pat: &str) -> bool;
328
329 fn ansi_ends_with(&self, pat: &str) -> bool;
347
348 fn ansi_trim(&self) -> Cow<'_, str>;
363
364 fn ansi_strip(&self) -> Cow<'_, str>;
378
379 fn ansi_has_any(&self) -> bool;
394}
395
396impl AnsiStr for str {
397 fn ansi_get<I>(&self, i: I) -> Option<Cow<'_, str>>
398 where
399 I: RangeBounds<usize>,
400 {
401 let (lower, upper) = bounds_to_usize(i.start_bound(), i.end_bound());
402 self::get(self, Some(lower), upper)
403 }
404
405 fn ansi_cut<I>(&self, i: I) -> Cow<'_, str>
406 where
407 I: RangeBounds<usize>,
408 {
409 self::cut(self, i)
410 }
411
412 fn ansi_is_char_boundary(&self, index: usize) -> bool {
413 str::is_char_boundary(&self.ansi_strip(), index)
414 }
415
416 fn ansi_find(&self, pat: &str) -> Option<usize> {
417 self::find(self, pat)
418 }
419
420 fn ansi_strip_prefix(&self, prefix: &str) -> Option<Cow<'_, str>> {
421 self::strip_prefix(self, prefix)
422 }
423
424 fn ansi_strip_suffix(&self, suffix: &str) -> Option<Cow<'_, str>> {
425 self::strip_suffix(self, suffix)
426 }
427
428 fn ansi_split_at(&self, mid: usize) -> (Cow<'_, str>, Cow<'_, str>) {
429 self::split_at(self, mid)
430 }
431
432 fn ansi_starts_with(&self, pat: &str) -> bool {
433 self::starts_with(self, pat)
434 }
435
436 fn ansi_ends_with(&self, pat: &str) -> bool {
437 self::ends_with(self, pat)
438 }
439
440 fn ansi_trim(&self) -> Cow<'_, str> {
441 self::trim(self)
442 }
443
444 fn ansi_strip(&self) -> Cow<'_, str> {
445 strip_ansi_sequences(self)
446 }
447
448 fn ansi_has_any(&self) -> bool {
449 self::has_any(self)
450 }
451
452 fn ansi_split<'a>(&'a self, pat: &'a str) -> AnsiSplit<'a> {
453 AnsiSplit::new(pat, self)
454 }
455}
456
457impl AnsiStr for String {
458 fn ansi_get<I>(&self, i: I) -> Option<Cow<'_, str>>
459 where
460 I: RangeBounds<usize>,
461 {
462 AnsiStr::ansi_get(self.as_str(), i)
463 }
464
465 fn ansi_cut<I>(&self, i: I) -> Cow<'_, str>
466 where
467 I: RangeBounds<usize>,
468 {
469 AnsiStr::ansi_cut(self.as_str(), i)
470 }
471
472 fn ansi_is_char_boundary(&self, index: usize) -> bool {
473 AnsiStr::ansi_is_char_boundary(self.as_str(), index)
474 }
475
476 fn ansi_find(&self, pat: &str) -> Option<usize> {
477 AnsiStr::ansi_find(self.as_str(), pat)
478 }
479
480 fn ansi_strip_prefix(&self, prefix: &str) -> Option<Cow<'_, str>> {
481 AnsiStr::ansi_strip_prefix(self.as_str(), prefix)
482 }
483
484 fn ansi_strip_suffix(&self, suffix: &str) -> Option<Cow<'_, str>> {
485 AnsiStr::ansi_strip_suffix(self.as_str(), suffix)
486 }
487
488 fn ansi_split_at(&self, mid: usize) -> (Cow<'_, str>, Cow<'_, str>) {
489 AnsiStr::ansi_split_at(self.as_str(), mid)
490 }
491
492 fn ansi_starts_with(&self, pat: &str) -> bool {
493 AnsiStr::ansi_starts_with(self.as_str(), pat)
494 }
495
496 fn ansi_ends_with(&self, pat: &str) -> bool {
497 AnsiStr::ansi_ends_with(self.as_str(), pat)
498 }
499
500 fn ansi_trim(&self) -> Cow<'_, str> {
501 AnsiStr::ansi_trim(self.as_str())
502 }
503
504 fn ansi_strip(&self) -> Cow<'_, str> {
505 AnsiStr::ansi_strip(self.as_str())
506 }
507
508 fn ansi_has_any(&self) -> bool {
509 AnsiStr::ansi_has_any(self.as_str())
510 }
511
512 fn ansi_split<'a>(&'a self, pat: &'a str) -> AnsiSplit<'a> {
513 AnsiStr::ansi_split(self.as_str(), pat)
514 }
515}
516
517macro_rules! write_list {
518 ($b:expr, $($c:tt)*) => {{
519 $(
520 let result = write!($b, "{}", $c);
521 debug_assert!(result.is_ok());
522 )*
523 }};
524}
525
526fn cut<R>(text: &str, bounds: R) -> Cow<'_, str>
527where
528 R: RangeBounds<usize>,
529{
530 let (start, end) = bounds_to_usize(bounds.start_bound(), bounds.end_bound());
531
532 cut_str(text, start, end)
533}
534
535fn cut_str(text: &str, lower_bound: usize, upper_bound: Option<usize>) -> Cow<'_, str> {
536 let mut ansi_state = AnsiState::default();
537 let mut buf = String::new();
538 let mut index = 0;
539
540 let tokens = parse_ansi(text);
541 '_tokens_loop: for token in tokens {
542 let tkn = &text[token.start()..token.end()];
543
544 match token.kind() {
545 ElementKind::Text => {
546 let block_end_index = index + tkn.len();
547 if lower_bound > block_end_index {
548 index += tkn.len();
549 continue;
550 };
551
552 let mut start = 0;
553 if lower_bound > index {
554 start = lower_bound - index;
555 }
556
557 let mut end = tkn.len();
558 let mut done = false;
559 if let Some(upper_bound) = upper_bound {
560 if upper_bound >= index && upper_bound < block_end_index {
561 end = upper_bound - index;
562 done = true;
563 }
564 }
565
566 index += tkn.len();
567
568 match tkn.get(start..end) {
569 Some(text) => {
570 if done && index == text.len() && !ansi_state.has_any() {
571 return Cow::Borrowed(text);
572 }
573
574 buf.push_str(text);
575 if done {
576 break '_tokens_loop;
577 }
578 }
579 None => panic!("One of indexes are not on a UTF-8 code point boundary"),
580 }
581 }
582 ElementKind::Sgr => {
583 write_list!(buf, tkn);
584 update_ansi_state(&mut ansi_state, tkn);
585 }
586 _ => write_list!(buf, tkn),
587 }
588 }
589
590 write_ansi_postfix(&mut buf, &ansi_state).unwrap();
591
592 Cow::Owned(buf)
593}
594
595fn get(text: &str, lower_bound: Option<usize>, upper_bound: Option<usize>) -> Option<Cow<'_, str>> {
596 let mut ansi_state = AnsiState::default();
597 let tokens = parse_ansi(text);
598 let mut buf = String::new();
599 let mut index = 0;
600
601 '_tokens_loop: for token in tokens {
602 let tkn = &text[token.start()..token.end()];
603
604 match token.kind() {
605 ElementKind::Text => {
606 let block_end_index = index + tkn.len();
607 let mut start = 0;
608 if let Some(lower_bound) = lower_bound {
609 if lower_bound >= block_end_index {
610 index += tkn.len();
611 continue;
612 }
613
614 if lower_bound > index {
615 start = lower_bound - index;
616 index += start;
617 }
618 }
619
620 let mut end = tkn.len();
621 let mut done = false;
622 if let Some(upper_bound) = upper_bound {
623 if upper_bound >= index && upper_bound < block_end_index {
624 end = upper_bound - index;
625 done = true;
626 }
627 }
628
629 let text = tkn.get(start..end)?;
630
631 let is_first_iteration = done && index == 0;
632 if is_first_iteration && !ansi_state.has_any() {
633 return Some(Cow::Borrowed(text));
634 }
635
636 buf.push_str(text);
637 index += text.len();
638
639 if done {
640 break '_tokens_loop;
641 }
642 }
643 ElementKind::Sgr => {
644 write_list!(buf, tkn);
645 update_ansi_state(&mut ansi_state, tkn);
646 }
647 _ => write_list!(buf, tkn),
648 }
649 }
650
651 write_ansi_postfix(&mut buf, &ansi_state).unwrap();
652
653 Some(Cow::Owned(buf))
654}
655
656fn split_at(text: &str, mid: usize) -> (Cow<'_, str>, Cow<'_, str>) {
657 if !has_any(text) {
658 if mid >= text.len() {
659 return (Cow::Borrowed(text), Cow::Borrowed(""));
660 }
661
662 let (lhs, rhs) = text.split_at(mid);
663 return (Cow::Borrowed(lhs), Cow::Borrowed(rhs));
664 }
665
666 let mut ansi_state = AnsiState::default();
667 let mut lhs = String::new();
668 let mut rhs = String::new();
669 let mut index = 0;
670
671 '_tokens_loop: for token in parse_ansi(text) {
672 let tkn = &text[token.start()..token.end()];
673
674 match token.kind() {
675 ElementKind::Text => {
676 let mut left = None;
677 let mut right = None;
678
679 if index <= mid && index + tkn.len() > mid {
680 let need = mid - index;
681 left = Some(&tkn[..need]);
682 right = Some(&tkn[need..]);
683 } else if index <= mid {
684 left = Some(tkn);
685 } else {
686 right = Some(tkn);
687 }
688
689 if let Some(text) = left {
690 if !text.is_empty() {
691 write_ansi_prefix(&mut lhs, &ansi_state).unwrap();
692 lhs.push_str(text);
693 write_ansi_postfix(&mut lhs, &ansi_state).unwrap();
694 }
695 }
696
697 if let Some(text) = right {
698 if !text.is_empty() {
699 write_ansi_prefix(&mut rhs, &ansi_state).unwrap();
700 rhs.push_str(text);
701 write_ansi_postfix(&mut rhs, &ansi_state).unwrap();
702 }
703 }
704
705 index += tkn.len();
706 }
707 ElementKind::Sgr => update_ansi_state(&mut ansi_state, tkn),
708 _ => {
709 if index <= mid {
710 write_list!(lhs, tkn);
711 } else {
712 write_list!(rhs, tkn);
713 }
714 }
715 }
716 }
717
718 (Cow::Owned(lhs), Cow::Owned(rhs))
719}
720
721fn strip_prefix<'a>(text: &'a str, mut pat: &str) -> Option<Cow<'a, str>> {
722 if pat.is_empty() {
723 return Some(Cow::Borrowed(text));
724 }
725
726 if pat.len() > text.len() {
727 return None;
728 }
729
730 let mut buf = String::new();
731 let mut tokens = parse_ansi(text);
732
733 let token = tokens.next()?;
736 let tkn = &text[token.start()..token.end()];
737 match token.kind() {
738 ElementKind::Text => {
739 if pat.len() <= tkn.len() {
740 let text = text.strip_prefix(pat)?;
743 return Some(Cow::Borrowed(text));
744 }
745
746 let p = pat.get(..text.len())?;
747 let s = text.strip_prefix(p)?;
748 buf.push_str(s);
749
750 pat = &pat[text.len()..];
751 }
752 _ => write_list!(buf, tkn),
753 }
754
755 for token in tokens {
756 let tkn = &text[token.start()..token.end()];
757 match token.kind() {
758 ElementKind::Text => {
759 let is_stripped = pat.is_empty();
760 if is_stripped {
761 buf.push_str(tkn);
762 continue;
763 }
764
765 if pat.len() <= tkn.len() {
766 let text = tkn.strip_prefix(pat)?;
767 buf.push_str(text);
768 pat = "";
769 continue;
770 }
771
772 let p = pat.get(..tkn.len())?;
773 let s = tkn.strip_prefix(p)?;
774 buf.push_str(s);
775
776 pat = &pat[tkn.len()..];
778 }
779 _ => write_list!(buf, tkn),
781 }
782 }
783
784 Some(Cow::Owned(buf))
785}
786
787fn strip_suffix<'a>(text: &'a str, mut pat: &str) -> Option<Cow<'a, str>> {
788 if pat.is_empty() {
789 return Some(Cow::Borrowed(text));
790 }
791
792 if pat.len() > text.len() {
793 return None;
794 }
795
796 #[allow(clippy::needless_collect)]
797 let tokens: Vec<_> = parse_ansi(text).collect();
798 let mut rev_tokens = tokens.into_iter().rev();
799 let mut buf = String::new();
800
801 let token = rev_tokens.next()?;
805 let tkn = &text[token.start()..token.end()];
806 match token.kind() {
807 ElementKind::Text => {
808 if pat.len() <= tkn.len() {
809 let text = text.strip_suffix(pat)?;
812 return Some(Cow::Borrowed(text));
813 }
814
815 let split_index = pat.len() - text.len();
816 let p = pat.get(split_index..)?;
817 let text = text.strip_suffix(p)?;
818 buf.insert_str(0, text);
819
820 pat = &pat[..split_index];
822 }
823 _ => write_list!(buf, tkn),
824 }
825
826 for token in rev_tokens {
827 let tkn = &text[token.start()..token.end()];
828 match token.kind() {
829 ElementKind::Text => {
830 let is_stripped = pat.is_empty();
831 if is_stripped {
832 buf.insert_str(0, tkn);
833 continue;
834 }
835
836 if pat.len() <= tkn.len() {
837 let text = tkn.strip_suffix(pat)?;
838 buf.insert_str(0, text);
839 pat = "";
840 continue;
841 }
842
843 let split_index = pat.len() - tkn.len();
844 let p = pat.get(split_index..)?;
845 let text = tkn.strip_suffix(p)?;
846 buf.insert_str(0, text);
847
848 pat = &pat[..split_index];
850 }
851 _ => buf.insert_str(0, tkn),
852 }
853 }
854
855 Some(Cow::Owned(buf))
856}
857
858fn starts_with(text: &str, mut pat: &str) -> bool {
859 if pat.is_empty() {
860 return true;
861 }
862
863 for token in parse_ansi(text) {
864 if token.kind() != ElementKind::Text {
865 continue;
866 }
867
868 let text = &text[token.start()..token.end()];
869 if pat.len() <= text.len() {
870 return text.starts_with(pat);
871 }
872
873 match pat.get(..text.len()) {
875 Some(p) => {
876 if !text.starts_with(p) {
877 return false;
878 }
879
880 pat = &pat[text.len()..];
882 if pat.is_empty() {
883 return true;
884 }
885 }
886 None => return false,
887 }
888 }
889
890 #[allow(clippy::let_and_return)]
891 let pattern_checked = pat.is_empty();
892 pattern_checked
893}
894
895fn ends_with(text: &str, pat: &str) -> bool {
896 text.ansi_strip().ends_with(pat)
898}
899
900fn trim(text: &str) -> Cow<'_, str> {
901 if !has_any(text) {
902 return Cow::Borrowed(text.trim());
903 }
904
905 let mut buf = String::new();
906 let mut buf_ansi = String::new();
907 let mut trimmed = false;
908
909 for token in parse_ansi(text) {
910 match token.kind() {
911 ElementKind::Text => {
912 let mut text = &text[token.start()..token.end()];
913
914 if !buf_ansi.is_empty() {
915 buf.push_str(&buf_ansi);
916 buf_ansi.clear();
917 }
918
919 if !trimmed {
920 text = text.trim_start();
921 if !text.is_empty() {
922 trimmed = true;
923 }
924 }
925
926 buf.push_str(text);
927 }
928 _ => {
929 let seq = &text[token.start()..token.end()];
930 write_list!(buf_ansi, seq);
931 }
932 }
933 }
934
935 let mut buf = buf.trim_end().to_owned();
937
938 if !buf_ansi.is_empty() {
939 buf.push_str(&buf_ansi);
940 }
941
942 Cow::Owned(buf)
943}
944
945fn find(text: &str, pat: &str) -> Option<usize> {
946 text.ansi_strip().find(pat)
948}
949
950fn has_any(text: &str) -> bool {
951 for token in parse_ansi(text) {
952 if token.kind() != ElementKind::Text {
953 return true;
954 }
955 }
956
957 false
958}
959
960fn strip_ansi_sequences(text: &str) -> Cow<'_, str> {
961 let mut buf = String::new();
962 let mut tokens = parse_ansi(text);
963
964 {
965 let t1 = match tokens.next() {
969 Some(t) => t,
970 None => return Cow::Borrowed(""),
971 };
972
973 match tokens.next() {
974 Some(t2) => {
975 if t1.kind() == ElementKind::Text {
976 let s = &text[t1.start()..t1.end()];
977 buf.push_str(s);
978 }
979
980 if t2.kind() == ElementKind::Text {
981 let s = &text[t2.start()..t2.end()];
982 buf.push_str(s);
983 }
984 }
985 None => {
986 return match t1.kind() {
987 ElementKind::Text => {
988 let s = &text[t1.start()..t1.end()];
989 Cow::Borrowed(s)
990 }
991 _ => Cow::Borrowed(""),
992 }
993 }
994 };
995 }
996
997 for token in tokens {
998 if token.kind() == ElementKind::Text {
999 let text = &text[token.start()..token.end()];
1000 buf.push_str(text);
1001 }
1002 }
1003
1004 Cow::Owned(buf)
1005}
1006
1007pub struct AnsiSplit<'a> {
1010 split_iter: std::str::Split<'a, &'a str>,
1011 ansi_state: AnsiState,
1012}
1013
1014impl<'a> AnsiSplit<'a> {
1015 fn new(pat: &'a str, text: &'a str) -> Self {
1016 Self {
1017 ansi_state: AnsiState::default(),
1018 split_iter: text.split(pat),
1019 }
1020 }
1021}
1022
1023impl<'a> Iterator for AnsiSplit<'a> {
1024 type Item = Cow<'a, str>;
1025
1026 fn next(&mut self) -> Option<Self::Item> {
1027 let mut part = Cow::Borrowed(self.split_iter.next()?);
1028 if part.is_empty() {
1029 return Some(part);
1030 }
1031
1032 if self.ansi_state.has_any() {
1033 let mut part_o = String::new();
1034 write_ansi_prefix(&mut part_o, &self.ansi_state).unwrap();
1035 part_o.push_str(&part);
1036
1037 part = Cow::Owned(part_o);
1038 }
1039
1040 for token in parse_ansi(&part) {
1041 if token.kind() == ElementKind::Sgr {
1042 let seq = &part[token.start()..token.end()];
1043 update_ansi_state(&mut self.ansi_state, seq);
1044 }
1045 }
1046
1047 if self.ansi_state.has_any() {
1048 let mut part_o = part.into_owned();
1049 write_ansi_postfix(&mut part_o, &self.ansi_state).unwrap();
1050
1051 part = Cow::Owned(part_o);
1052 }
1053
1054 Some(part)
1055 }
1056}
1057
1058#[must_use]
1074pub fn get_blocks(text: &str) -> AnsiBlockIter<'_> {
1075 AnsiBlockIter {
1076 buf: None,
1077 state: AnsiState::default(),
1078 tokens: parse_ansi(text),
1079 text,
1080 }
1081}
1082
1083pub struct AnsiBlockIter<'a> {
1086 text: &'a str,
1087 tokens: AnsiIterator<'a>,
1088 buf: Option<String>,
1089 state: AnsiState,
1090}
1091
1092impl<'a> Iterator for AnsiBlockIter<'a> {
1093 type Item = AnsiBlock<'a>;
1094
1095 fn next(&mut self) -> Option<Self::Item> {
1096 loop {
1097 let token = self.tokens.next()?;
1098 match token.kind() {
1099 ElementKind::Text => {
1100 let text = &self.text[token.start()..token.end()];
1101 let text = match self.buf.take() {
1103 Some(mut buf) => {
1104 buf.push_str(text);
1105 Cow::Owned(buf)
1106 }
1107 None => Cow::Borrowed(text),
1108 };
1109
1110 return Some(AnsiBlock::new(text, self.state));
1111 }
1112 ElementKind::Sgr => {
1113 let seq = &self.text[token.start()..token.end()];
1114 update_ansi_state(&mut self.state, seq);
1115 }
1116 _ => {
1117 let buf = match self.buf.as_mut() {
1118 Some(buf) => buf,
1119 None => {
1120 self.buf = Some(String::new());
1121 self.buf.as_mut().unwrap()
1122 }
1123 };
1124
1125 let seq = &self.text[token.start()..token.end()];
1126 write_list!(buf, seq);
1127 }
1128 }
1129 }
1130 }
1131}
1132
1133#[derive(Debug, Clone, PartialEq, Eq)]
1135pub struct AnsiBlock<'a> {
1136 text: Cow<'a, str>,
1137 state: Style,
1138}
1139
1140impl<'a> AnsiBlock<'a> {
1141 fn new(text: Cow<'a, str>, state: AnsiState) -> Self {
1142 Self {
1143 text,
1144 state: Style(state),
1145 }
1146 }
1147
1148 pub fn text(&self) -> &str {
1150 self.text.as_ref()
1151 }
1152
1153 pub fn has_ansi(&self) -> bool {
1155 self.state.0.has_any()
1156 }
1157
1158 pub fn style(&self) -> &Style {
1160 &self.state
1161 }
1162}
1163
1164pub struct AnsiSequenceStart<'a>(&'a AnsiState);
1167
1168impl std::fmt::Display for AnsiSequenceStart<'_> {
1169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1170 if !self.0.has_any() {
1171 return Ok(());
1172 }
1173
1174 write_ansi_prefix(f, self.0)
1175 }
1176}
1177
1178pub struct AnsiSequenceEnd<'a>(&'a AnsiState);
1181
1182impl AnsiSequenceEnd<'_> {
1183 pub const RESET_ALL: &'static str = "\u{1b}[0m";
1185}
1186
1187impl std::fmt::Display for AnsiSequenceEnd<'_> {
1188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1189 if !self.0.has_any() {
1190 return Ok(());
1191 }
1192
1193 write_ansi_postfix(f, self.0)
1194 }
1195}
1196
1197#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1199pub struct Style(AnsiState);
1200
1201impl Style {
1202 #[must_use]
1204 pub fn start(&self) -> AnsiSequenceStart<'_> {
1205 AnsiSequenceStart(&self.0)
1206 }
1207
1208 #[must_use]
1210 pub fn end(&self) -> AnsiSequenceEnd<'_> {
1211 AnsiSequenceEnd(&self.0)
1212 }
1213
1214 pub fn foreground(&self) -> Option<Color> {
1216 self.0.fg_color.map(Color::from)
1217 }
1218
1219 pub fn background(&self) -> Option<Color> {
1221 self.0.bg_color.map(Color::from)
1222 }
1223}
1224
1225macro_rules! style_method {
1226 ($name:ident, $field:ident) => {
1227 #[doc = stringify!($name)]
1229 pub fn $name(&self) -> bool {
1231 let AnsiState { $field, .. } = self.0;
1232 $field
1233 }
1234 };
1235}
1236
1237#[rustfmt::skip]
1238impl Style {
1239 style_method!(is_bold, bold);
1240 style_method!(is_faint, faint);
1241 style_method!(is_italic, italic);
1242 style_method!(is_underline, underline);
1243 style_method!(is_slow_blink, slow_blink);
1244 style_method!(is_rapid_blink, rapid_blink);
1245 style_method!(is_inverse, inverse);
1246 style_method!(is_hide, hide);
1247 style_method!(is_crossedout, crossedout);
1248 style_method!(is_fraktur, fraktur);
1249}
1250
1251#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1257pub enum Color {
1258 Black,
1263
1264 BrightBlack,
1266
1267 Red,
1269
1270 BrightRed,
1272
1273 Green,
1275
1276 BrightGreen,
1278
1279 Yellow,
1281
1282 BrightYellow,
1284
1285 Blue,
1287
1288 BrightBlue,
1290
1291 Purple,
1293
1294 BrightPurple,
1296
1297 Magenta,
1299
1300 BrightMagenta,
1302
1303 Cyan,
1305
1306 BrightCyan,
1308
1309 White,
1314
1315 BrightWhite,
1317
1318 Fixed(u8),
1335
1336 Rgb(u8, u8, u8),
1338}
1339
1340impl From<AnsiColor> for Color {
1341 fn from(clr: AnsiColor) -> Self {
1342 match clr {
1343 AnsiColor::Bit4(i) => match i {
1344 30 | 40 => Self::Black,
1345 31 | 41 => Self::Red,
1346 32 | 42 => Self::Green,
1347 33 | 43 => Self::Yellow,
1348 34 | 44 => Self::Blue,
1349 35 | 45 => Self::Magenta,
1350 36 | 46 => Self::Cyan,
1351 37 | 47 => Self::White,
1352 90 | 100 => Self::BrightBlack,
1353 91 | 101 => Self::BrightRed,
1354 92 | 102 => Self::BrightGreen,
1355 93 | 103 => Self::BrightYellow,
1356 94 | 104 => Self::BrightBlue,
1357 95 | 105 => Self::BrightMagenta,
1358 96 | 106 => Self::BrightCyan,
1359 97 | 107 => Self::BrightWhite,
1360 n => Self::Fixed(n),
1361 },
1362 AnsiColor::Bit8(i) => Self::Fixed(i),
1363 AnsiColor::Bit24 { r, g, b } => Self::Rgb(r, g, b),
1364 }
1365 }
1366}
1367
1368#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1369struct AnsiState {
1370 fg_color: Option<AnsiColor>,
1371 bg_color: Option<AnsiColor>,
1372 undr_color: Option<AnsiColor>,
1373 bold: bool,
1374 faint: bool,
1375 italic: bool,
1376 underline: bool,
1377 double_underline: bool,
1378 slow_blink: bool,
1379 rapid_blink: bool,
1380 inverse: bool,
1381 hide: bool,
1382 crossedout: bool,
1383 reset: bool,
1384 framed: bool,
1385 encircled: bool,
1386 font: Option<u8>,
1387 fraktur: bool,
1388 proportional_spacing: bool,
1389 overlined: bool,
1390 igrm_underline: bool,
1391 igrm_double_underline: bool,
1392 igrm_overline: bool,
1393 igrm_double_overline: bool,
1394 igrm_stress_marking: bool,
1395 superscript: bool,
1396 subscript: bool,
1397 unknown: bool,
1398}
1399
1400impl AnsiState {
1401 fn has_any(&self) -> bool {
1402 self.fg_color.is_some()
1403 || self.bg_color.is_some()
1404 || self.undr_color.is_some()
1405 || self.bold
1406 || self.crossedout
1407 || self.double_underline
1408 || self.encircled
1409 || self.faint
1410 || self.fraktur
1411 || self.framed
1412 || self.hide
1413 || self.inverse
1414 || self.italic
1415 || self.overlined
1416 || self.proportional_spacing
1417 || self.rapid_blink
1418 || self.slow_blink
1419 || self.underline
1420 || self.subscript
1421 || self.superscript
1422 || self.igrm_double_overline
1423 || self.igrm_double_underline
1424 || self.igrm_overline
1425 || self.igrm_stress_marking
1426 || self.igrm_underline
1427 || (self.reset && self.unknown)
1428 }
1429}
1430
1431fn update_ansi_state(state: &mut AnsiState, mode: &str) {
1432 let mode = {
1433 let mode = mode
1434 .strip_prefix("\u{1b}[")
1435 .and_then(|mode| mode.strip_suffix('m'));
1436 match mode {
1437 Some(mode) => mode,
1438 _ => {
1439 debug_assert!(false);
1441 return;
1442 }
1443 }
1444 };
1445
1446 let mut sequences = mode.split(';');
1447 while let Some(seq) = sequences.next() {
1448 let exited = parse_sgr(state, seq, &mut sequences);
1449 if exited {
1450 break;
1451 }
1452 }
1453}
1454
1455fn parse_sgr<'a>(
1456 state: &mut AnsiState,
1457 sequence: &str,
1458 next_sequences: &mut impl Iterator<Item = &'a str>,
1459) -> bool {
1460 match sequence {
1461 "0" => {
1462 *state = AnsiState::default();
1463 state.reset = true;
1464 }
1465 "1" => state.bold = true,
1466 "2" => state.faint = true,
1467 "3" => state.italic = true,
1468 "4" => state.underline = true,
1469 "5" => state.slow_blink = true,
1470 "6" => state.rapid_blink = true,
1471 "7" => state.inverse = true,
1472 "8" => state.hide = true,
1473 "9" => state.crossedout = true,
1474 "10" => state.font = None,
1475 "11" => state.font = Some(11),
1476 "12" => state.font = Some(12),
1477 "13" => state.font = Some(13),
1478 "14" => state.font = Some(14),
1479 "15" => state.font = Some(15),
1480 "16" => state.font = Some(16),
1481 "17" => state.font = Some(17),
1482 "18" => state.font = Some(18),
1483 "19" => state.font = Some(19),
1484 "20" => state.fraktur = true,
1485 "21" => state.double_underline = true,
1486 "22" => {
1487 state.faint = false;
1488 state.bold = false;
1489 }
1490 "23" => {
1491 state.italic = false;
1492 }
1493 "24" => {
1494 state.underline = false;
1495 state.double_underline = false;
1496 }
1497 "25" => {
1498 state.slow_blink = false;
1499 state.rapid_blink = false;
1500 }
1501 "26" => {
1502 state.proportional_spacing = true;
1503 }
1504 "27" => {
1505 state.inverse = false;
1506 }
1507 "28" => {
1508 state.hide = false;
1509 }
1510 "29" => {
1511 state.crossedout = false;
1512 }
1513 "30" => state.fg_color = Some(AnsiColor::Bit4(30)),
1514 "31" => state.fg_color = Some(AnsiColor::Bit4(31)),
1515 "32" => state.fg_color = Some(AnsiColor::Bit4(32)),
1516 "33" => state.fg_color = Some(AnsiColor::Bit4(33)),
1517 "34" => state.fg_color = Some(AnsiColor::Bit4(34)),
1518 "35" => state.fg_color = Some(AnsiColor::Bit4(35)),
1519 "36" => state.fg_color = Some(AnsiColor::Bit4(36)),
1520 "37" => state.fg_color = Some(AnsiColor::Bit4(37)),
1521 "38" => {
1522 let clr = parse_sgr_color(next_sequences);
1523 if clr.is_none() {
1524 return false;
1525 }
1526
1527 state.fg_color = clr;
1528 }
1529 "39" => state.fg_color = None,
1530 "40" => state.bg_color = Some(AnsiColor::Bit4(40)),
1531 "41" => state.bg_color = Some(AnsiColor::Bit4(41)),
1532 "42" => state.bg_color = Some(AnsiColor::Bit4(42)),
1533 "43" => state.bg_color = Some(AnsiColor::Bit4(43)),
1534 "44" => state.bg_color = Some(AnsiColor::Bit4(44)),
1535 "45" => state.bg_color = Some(AnsiColor::Bit4(45)),
1536 "46" => state.bg_color = Some(AnsiColor::Bit4(46)),
1537 "47" => state.bg_color = Some(AnsiColor::Bit4(47)),
1538 "48" => {
1539 let clr = parse_sgr_color(next_sequences);
1540 if clr.is_none() {
1541 return false;
1542 }
1543
1544 state.bg_color = clr;
1545 }
1546 "49" => state.bg_color = None,
1547 "50" => state.proportional_spacing = false,
1548 "51" => state.framed = true,
1549 "52" => state.encircled = true,
1550 "53" => state.overlined = true,
1551 "54" => {
1552 state.encircled = false;
1553 state.framed = false;
1554 }
1555 "55" => state.overlined = false,
1556 "58" => {
1557 let clr = parse_sgr_color(next_sequences);
1558 if clr.is_none() {
1559 return false;
1560 }
1561
1562 state.undr_color = clr;
1563 }
1564 "59" => state.undr_color = None,
1565 "60" => state.igrm_underline = true,
1566 "61" => state.igrm_double_underline = true,
1567 "62" => state.igrm_overline = true,
1568 "63" => state.igrm_double_overline = true,
1569 "64" => state.igrm_stress_marking = true,
1570 "65" => {
1571 state.igrm_underline = false;
1572 state.igrm_double_underline = false;
1573 state.igrm_overline = false;
1574 state.igrm_double_overline = false;
1575 state.igrm_stress_marking = false;
1576 }
1577 "73" => state.superscript = true,
1578 "74" => state.subscript = true,
1579 "75" => {
1580 state.subscript = false;
1581 state.superscript = false;
1582 }
1583 "90" => state.fg_color = Some(AnsiColor::Bit4(90)),
1584 "91" => state.fg_color = Some(AnsiColor::Bit4(91)),
1585 "92" => state.fg_color = Some(AnsiColor::Bit4(92)),
1586 "93" => state.fg_color = Some(AnsiColor::Bit4(93)),
1587 "94" => state.fg_color = Some(AnsiColor::Bit4(94)),
1588 "95" => state.fg_color = Some(AnsiColor::Bit4(95)),
1589 "96" => state.fg_color = Some(AnsiColor::Bit4(96)),
1590 "97" => state.fg_color = Some(AnsiColor::Bit4(97)),
1591 "100" => state.bg_color = Some(AnsiColor::Bit4(100)),
1592 "101" => state.bg_color = Some(AnsiColor::Bit4(101)),
1593 "102" => state.bg_color = Some(AnsiColor::Bit4(102)),
1594 "103" => state.bg_color = Some(AnsiColor::Bit4(103)),
1595 "104" => state.bg_color = Some(AnsiColor::Bit4(104)),
1596 "105" => state.bg_color = Some(AnsiColor::Bit4(105)),
1597 "106" => state.bg_color = Some(AnsiColor::Bit4(106)),
1598 "107" => state.bg_color = Some(AnsiColor::Bit4(107)),
1599 _ => {
1600 state.unknown = true;
1601 }
1602 }
1603
1604 false
1605}
1606
1607fn parse_sgr_color<'a>(sequence: &mut impl Iterator<Item = &'a str>) -> Option<AnsiColor> {
1608 let n = sequence.next()?;
1609 if n == "2" {
1610 let r = sequence.next()?.parse::<u8>().unwrap_or(0);
1611 let g = sequence.next()?.parse::<u8>().unwrap_or(0);
1612 let b = sequence.next()?.parse::<u8>().unwrap_or(0);
1613
1614 Some(AnsiColor::Bit24 { r, g, b })
1615 } else if n == "5" {
1616 let index = sequence.next()?.parse::<u8>().unwrap_or(0);
1617 Some(AnsiColor::Bit8(index))
1618 } else {
1619 None
1620 }
1621}
1622
1623macro_rules! emit_block {
1624 ($f:expr, $b:block) => {
1625 #[allow(unused_macros)]
1656 macro_rules! emit {
1657 ($foo:expr) => {
1658 $f.write_str("\u{1b}[")?;
1659 $foo?;
1660 $f.write_char('m')?;
1661 };
1662 }
1663
1664 #[allow(unused_macros)]
1665 macro_rules! emit_str {
1666 ($foo:expr) => {
1667 $f.write_str("\u{1b}[")?;
1668 $f.write_str($foo)?;
1669 $f.write_char('m')?;
1670 };
1671 }
1672
1673 #[allow(unused_macros)]
1674 macro_rules! cond {
1675 ($foo:expr, $do:expr) => {
1676 if $foo {
1677 $do;
1678 }
1679 };
1680
1681 ($name:ident => $foo:expr, $do:expr) => {
1682 if let Some($name) = $foo {
1683 $do;
1684 }
1685 };
1686 }
1687
1688 $b
1689 };
1690}
1691
1692fn write_ansi_prefix(mut f: impl std::fmt::Write, state: &AnsiState) -> std::fmt::Result {
1693 #[rustfmt::skip]
1694 emit_block!(f, {
1695 cond!(state.bold, emit_str!("1"));
1696 cond!(state.faint, emit_str!("2"));
1697 cond!(state.italic, emit_str!("3"));
1698 cond!(state.underline, emit_str!("4"));
1699 cond!(state.slow_blink, emit_str!("5"));
1700 cond!(state.rapid_blink, emit_str!("6"));
1701 cond!(state.inverse, emit_str!("7"));
1702 cond!(state.hide, emit_str!("8"));
1703 cond!(state.crossedout, emit_str!("9"));
1704 cond!(font => state.font, emit!(f.write_fmt(format_args!("{}", font))));
1705 cond!(state.fraktur, emit_str!("20"));
1706 cond!(state.double_underline, emit_str!("21"));
1707 cond!(state.proportional_spacing, emit_str!("26"));
1708 cond!(color => &state.fg_color, emit!(write_color(&mut f, color, &ColorType::Fg)));
1709 cond!(color => &state.bg_color, emit!(write_color(&mut f, color, &ColorType::Bg)));
1710 cond!(color => &state.undr_color, emit!(write_color(&mut f, color, &ColorType::Undr)));
1711 cond!(state.framed, emit_str!("51"));
1712 cond!(state.encircled, emit_str!("52"));
1713 cond!(state.overlined, emit_str!("53"));
1714 cond!(state.igrm_underline, emit_str!("60"));
1715 cond!(state.igrm_double_underline, emit_str!("61"));
1716 cond!(state.igrm_overline, emit_str!("62"));
1717 cond!(state.igrm_double_overline, emit_str!("63"));
1718 cond!(state.igrm_stress_marking, emit_str!("64"));
1719 cond!(state.superscript, emit_str!("73"));
1720 cond!(state.subscript, emit_str!("74"));
1721 });
1722
1723 Ok(())
1724}
1725
1726fn write_ansi_postfix(mut f: impl std::fmt::Write, state: &AnsiState) -> std::fmt::Result {
1727 #[rustfmt::skip]
1728 emit_block!(f, {
1729 cond!(state.unknown && state.reset, emit_str!("0"));
1730 cond!(state.font.is_some(), emit_str!("10"));
1731 cond!(state.bold || state.faint, emit_str!("22"));
1732 cond!(state.italic || state.fraktur, emit_str!("23"));
1733 cond!(state.underline || state.double_underline, emit_str!("24"));
1734 cond!(state.slow_blink || state.rapid_blink, emit_str!("25"));
1735 cond!(state.inverse, emit_str!("27"));
1736 cond!(state.hide, emit_str!("28"));
1737 cond!(state.crossedout, emit_str!("29"));
1738 cond!(state.fg_color.is_some(), emit_str!("39"));
1739 cond!(state.bg_color.is_some(), emit_str!("49"));
1740 cond!(state.proportional_spacing, emit_str!("50"));
1741 cond!(state.encircled || state.framed, emit_str!("54"));
1742 cond!(state.overlined, emit_str!("55"));
1743 cond!(state.igrm_underline ||
1744 state.igrm_double_underline ||
1745 state.igrm_overline ||
1746 state.igrm_double_overline ||
1747 state.igrm_stress_marking, emit_str!("65"));
1748 cond!(state.undr_color.is_some(), emit_str!("59"));
1749 cond!(state.subscript || state.superscript, emit_str!("75"));
1750 cond!(state.unknown, emit_str!("0"));
1751 });
1752
1753 Ok(())
1754}
1755
1756enum ColorType {
1757 Bg,
1758 Fg,
1759 Undr,
1760}
1761
1762fn write_color(mut f: impl std::fmt::Write, color: &AnsiColor, ct: &ColorType) -> std::fmt::Result {
1763 match *color {
1764 AnsiColor::Bit4(index) => write!(f, "{}", index),
1765 AnsiColor::Bit8(index) => f.write_fmt(format_args!("{};5;{}", color_type(ct), index)),
1766 AnsiColor::Bit24 { r, g, b } => {
1767 f.write_fmt(format_args!("{};2;{};{};{}", color_type(ct), r, g, b))
1768 }
1769 }
1770}
1771
1772fn color_type(color_type: &ColorType) -> &'static str {
1773 match color_type {
1774 ColorType::Bg => "48",
1775 ColorType::Fg => "38",
1776 ColorType::Undr => "58",
1777 }
1778}
1779
1780fn bounds_to_usize(left: Bound<&usize>, right: Bound<&usize>) -> (usize, Option<usize>) {
1781 match (left, right) {
1782 (Bound::Included(x), Bound::Included(y)) => (*x, Some(y + 1)),
1783 (Bound::Included(x), Bound::Excluded(y)) => (*x, Some(*y)),
1784 (Bound::Included(x), Bound::Unbounded) => (*x, None),
1785 (Bound::Unbounded, Bound::Unbounded) => (0, None),
1786 (Bound::Unbounded, Bound::Included(y)) => (0, Some(y + 1)),
1787 (Bound::Unbounded, Bound::Excluded(y)) => (0, Some(*y)),
1788 (Bound::Excluded(_), Bound::Unbounded)
1789 | (Bound::Excluded(_), Bound::Included(_))
1790 | (Bound::Excluded(_), Bound::Excluded(_)) => {
1791 unreachable!("A start bound can't be excluded")
1792 }
1793 }
1794}
1795
1796#[cfg(test)]
1797mod tests {
1798 use super::*;
1799
1800 #[test]
1844 fn cut_colored_fg_test() {
1845 let colored_s = "\u{1b}[30mTEXT\u{1b}[39m";
1846 assert_eq!(colored_s, colored_s.ansi_cut(..));
1847 assert_eq!(colored_s, colored_s.ansi_cut(0..4));
1848 assert_eq!("\u{1b}[30mEXT\u{1b}[39m", colored_s.ansi_cut(1..));
1849 assert_eq!("\u{1b}[30mTEX\u{1b}[39m", colored_s.ansi_cut(..3));
1850 assert_eq!("\u{1b}[30mEX\u{1b}[39m", colored_s.ansi_cut(1..3));
1851
1852 assert_eq!("TEXT", strip_ansi_sequences(&colored_s.ansi_cut(..)));
1853 assert_eq!("TEX", strip_ansi_sequences(&colored_s.ansi_cut(..3)));
1854 assert_eq!("EX", strip_ansi_sequences(&colored_s.ansi_cut(1..3)));
1855
1856 let colored_s = "\u{1b}[30mTEXT\u{1b}[39m \u{1b}[31mTEXT\u{1b}[39m";
1857 assert_eq!(colored_s, colored_s.ansi_cut(..));
1858 assert_eq!(colored_s, colored_s.ansi_cut(0..9));
1859 assert_eq!(
1860 "\u{1b}[30mXT\u{1b}[39m \u{1b}[31mTEXT\u{1b}[39m",
1861 colored_s.ansi_cut(2..)
1862 );
1863 assert_eq!(
1864 "\u{1b}[30mTEXT\u{1b}[39m \u{1b}[31mT\u{1b}[39m",
1865 colored_s.ansi_cut(..6)
1866 );
1867 assert_eq!(
1868 "\u{1b}[30mXT\u{1b}[39m \u{1b}[31mT\u{1b}[39m",
1869 colored_s.ansi_cut(2..6)
1870 );
1871
1872 assert_eq!("TEXT TEXT", strip_ansi_sequences(&colored_s.ansi_cut(..)));
1873 assert_eq!("TEXT T", strip_ansi_sequences(&colored_s.ansi_cut(..6)));
1874 assert_eq!("XT T", strip_ansi_sequences(&colored_s.ansi_cut(2..6)));
1875
1876 assert_eq!("\u{1b}[30m\u{1b}[39m", cut("\u{1b}[30m\u{1b}[39m", ..));
1877 }
1878
1879 #[test]
1880 fn cut_colored_bg_test() {
1881 let colored_s = "\u{1b}[40mTEXT\u{1b}[49m";
1882 assert_eq!(colored_s, colored_s.ansi_cut(..));
1883 assert_eq!(colored_s, colored_s.ansi_cut(0..4));
1884 assert_eq!("\u{1b}[40mEXT\u{1b}[49m", colored_s.ansi_cut(1..));
1885 assert_eq!("\u{1b}[40mTEX\u{1b}[49m", colored_s.ansi_cut(..3));
1886 assert_eq!("\u{1b}[40mEX\u{1b}[49m", colored_s.ansi_cut(1..3));
1887
1888 assert_eq!("\u{1b}[40m\u{1b}[49m", colored_s.ansi_cut(3..3));
1890
1891 assert_eq!("TEXT", strip_ansi_sequences(&colored_s.ansi_cut(..)));
1892 assert_eq!("TEX", strip_ansi_sequences(&colored_s.ansi_cut(..3)));
1893 assert_eq!("EX", strip_ansi_sequences(&colored_s.ansi_cut(1..3)));
1894
1895 let colored_s = "\u{1b}[40mTEXT\u{1b}[49m \u{1b}[41mTEXT\u{1b}[49m";
1896 assert_eq!(colored_s, colored_s.ansi_cut(..));
1897 assert_eq!(colored_s, colored_s.ansi_cut(0..9));
1898 assert_eq!(
1899 "\u{1b}[40mXT\u{1b}[49m \u{1b}[41mTEXT\u{1b}[49m",
1900 colored_s.ansi_cut(2..)
1901 );
1902 assert_eq!(
1903 "\u{1b}[40mTEXT\u{1b}[49m \u{1b}[41mT\u{1b}[49m",
1904 colored_s.ansi_cut(..6)
1905 );
1906 assert_eq!(
1907 "\u{1b}[40mXT\u{1b}[49m \u{1b}[41mT\u{1b}[49m",
1908 colored_s.ansi_cut(2..6)
1909 );
1910
1911 assert_eq!("TEXT TEXT", strip_ansi_sequences(&colored_s.ansi_cut(..)));
1912 assert_eq!("TEXT T", strip_ansi_sequences(&colored_s.ansi_cut(..6)));
1913 assert_eq!("XT T", strip_ansi_sequences(&colored_s.ansi_cut(2..6)));
1914
1915 assert_eq!("\u{1b}[40m\u{1b}[49m", cut("\u{1b}[40m\u{1b}[49m", ..));
1916 }
1917
1918 #[test]
1919 fn cut_colored_bg_fg_test() {
1920 let colored_s = "\u{1b}[31;40mTEXT\u{1b}[0m";
1921 assert_eq!(
1922 "\u{1b}[31;40m\u{1b}[39m\u{1b}[49m",
1923 colored_s.ansi_cut(0..0)
1924 );
1925 assert_eq!(colored_s, colored_s.ansi_cut(..));
1926 assert_eq!(colored_s, colored_s.ansi_cut(0..4));
1927 assert_eq!("\u{1b}[31;40mEXT\u{1b}[0m", colored_s.ansi_cut(1..));
1928 assert_eq!(
1929 "\u{1b}[31;40mTEX\u{1b}[39m\u{1b}[49m",
1930 colored_s.ansi_cut(..3)
1931 );
1932 assert_eq!(
1933 "\u{1b}[31;40mEX\u{1b}[39m\u{1b}[49m",
1934 colored_s.ansi_cut(1..3)
1935 );
1936
1937 assert_eq!("TEXT", strip_ansi_sequences(&colored_s.ansi_cut(..)));
1938 assert_eq!("TEX", strip_ansi_sequences(&colored_s.ansi_cut(..3)));
1939 assert_eq!("EX", strip_ansi_sequences(&colored_s.ansi_cut(1..3)));
1940
1941 let colored_s = "\u{1b}[31;40mTEXT\u{1b}[0m \u{1b}[34;42mTEXT\u{1b}[0m";
1942 assert_eq!(colored_s, colored_s.ansi_cut(..));
1943 assert_eq!(colored_s, colored_s.ansi_cut(0..9));
1944 assert_eq!(
1945 "\u{1b}[31;40mXT\u{1b}[0m \u{1b}[34;42mTEXT\u{1b}[0m",
1946 colored_s.ansi_cut(2..)
1947 );
1948 assert_eq!(
1949 "\u{1b}[31;40mTEXT\u{1b}[0m \u{1b}[34;42mT\u{1b}[39m\u{1b}[49m",
1950 colored_s.ansi_cut(..6)
1951 );
1952 assert_eq!(
1953 "\u{1b}[31;40mXT\u{1b}[0m \u{1b}[34;42mT\u{1b}[39m\u{1b}[49m",
1954 colored_s.ansi_cut(2..6)
1955 );
1956
1957 assert_eq!("TEXT TEXT", strip_ansi_sequences(&colored_s.ansi_cut(..)));
1958 assert_eq!("TEXT T", strip_ansi_sequences(&colored_s.ansi_cut(..6)));
1959 assert_eq!("XT T", strip_ansi_sequences(&colored_s.ansi_cut(2..6)));
1960
1961 assert_eq!("\u{1b}[40m\u{1b}[49m", cut("\u{1b}[40m\u{1b}[49m", ..));
1962 }
1963
1964 #[test]
1965 fn cut_keep_general_color_test() {
1966 assert_eq!(
1967 "\u{1b}[41m\u{1b}[30m\u{1b}[39m \u{1b}[34m12\u{1b}[39m\u{1b}[49m",
1968 "\u{1b}[41m\u{1b}[30msomething\u{1b}[39m \u{1b}[34m123123\u{1b}[39m\u{1b}[49m"
1969 .ansi_cut(9..12)
1970 );
1971 }
1972
1973 #[test]
1974 fn cut_no_colored_str() {
1975 assert_eq!("something", cut("something", ..));
1976 assert_eq!("som", cut("something", ..3));
1977 assert_eq!("some", cut("something", ..=3));
1978 assert_eq!("et", cut("something", 3..5));
1979 assert_eq!("eth", cut("something", 3..=5));
1980 assert_eq!("ething", cut("something", 3..));
1981 assert_eq!("something", cut("something", ..));
1982 assert_eq!("", cut("", ..));
1983 }
1984
1985 #[test]
1986 fn cut_dont_panic_on_exceeding_upper_bound() {
1987 assert_eq!("TEXT", cut("TEXT", ..50));
1988 assert_eq!("EXT", cut("TEXT", 1..50));
1989 assert_eq!(
1990 "\u{1b}[31;40mTEXT\u{1b}[0m",
1991 cut("\u{1b}[31;40mTEXT\u{1b}[0m", ..50)
1992 );
1993 assert_eq!(
1994 "\u{1b}[31;40mEXT\u{1b}[0m",
1995 cut("\u{1b}[31;40mTEXT\u{1b}[0m", 1..50)
1996 );
1997 }
1998
1999 #[test]
2000 fn cut_dont_panic_on_exceeding_lower_bound() {
2001 assert_eq!("", cut("TEXT", 10..));
2002 assert_eq!("", cut("TEXT", 10..50));
2003 }
2004
2005 #[test]
2006 #[should_panic = "One of indexes are not on a UTF-8 code point boundary"]
2007 fn cut_a_mid_of_emojie_2_test() {
2008 cut("😀", 1..2);
2009 }
2010
2011 #[test]
2012 #[should_panic = "One of indexes are not on a UTF-8 code point boundary"]
2013 fn cut_a_mid_of_emojie_1_test() {
2014 cut("😀", 1..);
2015 }
2016
2017 #[test]
2018 #[should_panic = "One of indexes are not on a UTF-8 code point boundary"]
2019 fn cut_a_mid_of_emojie_0_test() {
2020 cut("😀", ..1);
2021 }
2022
2023 #[test]
2024 fn cut_emojies_test() {
2025 let emojes = "😀😃😄😁😆😅😂🤣🥲😊";
2026 assert_eq!(emojes, emojes.ansi_cut(..));
2027 assert_eq!("😀", emojes.ansi_cut(..4));
2028 assert_eq!("😃😄", emojes.ansi_cut(4..12));
2029 assert_eq!("🤣🥲😊", emojes.ansi_cut(emojes.find('🤣').unwrap()..));
2030 }
2031
2032 #[test]
2033 fn cut_colored_x_x_test() {
2035 assert_ne!("", cut("\u{1b}[31;40mTEXT\u{1b}[0m", 3..3));
2036 assert_ne!(
2037 "",
2038 cut(
2039 "\u{1b}[31;40mTEXT\u{1b}[0m \u{1b}[34;42mTEXT\u{1b}[0m",
2040 1..1
2041 )
2042 );
2043 assert_ne!("", cut("\u{1b}[31;40mTEXT\u{1b}[0m", ..0));
2044
2045 assert_eq!("", cut("123", 0..0));
2046 assert_eq!(
2047 "\u{1b}[31;40m\u{1b}[39m\u{1b}[49m",
2048 "\u{1b}[31;40mTEXT\u{1b}[0m".ansi_cut(0..0)
2049 );
2050 }
2051
2052 #[test]
2053 fn cut_partially_colored_str_test() {
2054 let s = "zxc_\u{1b}[31;40mTEXT\u{1b}[0m_qwe";
2055 assert_eq!("zxc", s.ansi_cut(..3));
2056 assert_eq!("zxc_\u{1b}[31;40mT\u{1b}[39m\u{1b}[49m", s.ansi_cut(..5));
2057 assert_eq!("\u{1b}[31;40mEXT\u{1b}[0m_q", s.ansi_cut(5..10));
2058 assert_eq!("\u{1b}[31;40m\u{1b}[0m", s.ansi_cut(12..));
2059 }
2060
2061 #[test]
2062 fn ansi_get_test() {
2063 let text = "TEXT";
2064 assert_eq!(text.get(0..0).map(Cow::Borrowed), text.ansi_get(0..0));
2065 assert_eq!(Some(Cow::Borrowed("")), text.ansi_get(0..0));
2066 assert_eq!(text.get(0..1).map(Cow::Borrowed), text.ansi_get(0..1));
2067
2068 let text = "\u{1b}[30m123:456\u{1b}[39m";
2069 assert_eq!(Some("\u{1b}[30m\u{1b}[39m".into()), text.ansi_get(0..0));
2070 }
2071
2072 #[test]
2073 fn ansi_get_test_0() {
2074 let text = "\u{1b}[35m│\u{1b}[39m \u{1b}[1;32mcpu\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32m#\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32mname\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32mbrand\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32mfreq\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32mcpu_usage\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32mload_average\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32mvendor_id\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[35m│\u{1b}[39m";
2075 assert_eq!(
2076 text.ansi_get(105..).unwrap().ansi_strip(),
2077 Cow::Borrowed(text.ansi_strip().get(105..).unwrap())
2078 );
2079
2080 assert_eq!(text.ansi_get(105..).unwrap(), "\u{1b}[35m\u{1b}[39m\u{1b}[1;32m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[35m\u{1b}[39m\u{1b}[1;32m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[1;32m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[1;32m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[1;32m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[1;32m\u{1b}[0m\u{1b}[35m│\u{1b}[39m \u{1b}[1;32mload_average\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[1;32mvendor_id\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[35m│\u{1b}[39m");
2081 }
2082
2083 #[test]
2084 fn ansi_get_test_1() {
2085 let text = "\u{1b}[35m│\u{1b}[39m \u{1b}[35m│\u{1b}[39m \u{1b}[35m│\u{1b}[39m \u{1b}[1;36m1\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[37mcpu0\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[37m11th Gen Intel(R) Core(TM) i7-11850H @ 2.50GHz\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[32m8\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[31m0.0000\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[37m1.09, 1.44, 1.25\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[37mGenuineIntel\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[35m│\u{1b}[39m";
2086
2087 let result = text.ansi_get(..3).unwrap();
2088 assert_eq!(result.ansi_strip(), Cow::Borrowed("│"));
2089 assert_eq!(result, "\u{1b}[35m│\u{1b}[39m");
2090
2091 let result = text.ansi_get(123..).unwrap();
2092 assert_eq!(result.ansi_strip(), Cow::Borrowed("25 │ GenuineIntel │ │"));
2093 assert_eq!(result, "\u{1b}[35m\u{1b}[39m\u{1b}[35m\u{1b}[39m\u{1b}[35m\u{1b}[39m\u{1b}[1;36m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[37m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[37m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[32m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[31m\u{1b}[0m\u{1b}[35m\u{1b}[39m\u{1b}[37m25\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[37mGenuineIntel\u{1b}[0m \u{1b}[35m│\u{1b}[39m \u{1b}[35m│\u{1b}[39m");
2094 }
2095
2096 #[test]
2097 fn split_at_test() {
2098 {
2099 let colored_s = "\u{1b}[30mTEXT\u{1b}[39m";
2100 assert_eq!(("".into(), colored_s.into()), colored_s.ansi_split_at(0));
2101 assert_eq!(
2102 (
2103 "\u{1b}[30mTE\u{1b}[39m".into(),
2104 "\u{1b}[30mXT\u{1b}[39m".into()
2105 ),
2106 colored_s.ansi_split_at(2)
2107 );
2108 assert_eq!(
2109 ("\u{1b}[30mTEXT\u{1b}[39m".into(), "".into()),
2110 colored_s.ansi_split_at(4)
2111 );
2112 }
2113
2114 {
2115 for colored_s in [
2116 "\u{1b}[41m\u{1b}[30msomething\u{1b}[39m \u{1b}[34m123123\u{1b}[39m\u{1b}[49m",
2117 "\u{1b}[41;30msomething\u{1b}[39m \u{1b}[34m123123\u{1b}[39;49m",
2118 ] {
2119 assert_eq!(
2120 ("".into(), "\u{1b}[30m\u{1b}[41msomething\u{1b}[39m\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[34m\u{1b}[41m123123\u{1b}[39m\u{1b}[49m".into()),
2121 colored_s.ansi_split_at(0)
2122 );
2123 assert_eq!(
2124 ("\u{1b}[30m\u{1b}[41mso\u{1b}[39m\u{1b}[49m".into(), "\u{1b}[30m\u{1b}[41mmething\u{1b}[39m\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[34m\u{1b}[41m123123\u{1b}[39m\u{1b}[49m".into()),
2125 colored_s.ansi_split_at(2)
2126 );
2127 assert_eq!(
2128 (
2129 "\u{1b}[30m\u{1b}[41msomethi\u{1b}[39m\u{1b}[49m".into(),
2130 "\u{1b}[30m\u{1b}[41mng\u{1b}[39m\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[34m\u{1b}[41m123123\u{1b}[39m\u{1b}[49m".into(),
2131 ),
2132 colored_s.ansi_split_at(7)
2133 );
2134 }
2135 }
2136
2137 {
2138 let colored_s = "\u{1b}[30mTEXT\u{1b}[39m";
2139 assert_eq!(
2140 ("\u{1b}[30mTEXT\u{1b}[39m".into(), "".into()),
2141 colored_s.ansi_split_at(10)
2142 );
2143 }
2144 }
2145
2146 #[test]
2147 fn split_dont_panic_on_exceeding_mid() {
2148 assert_eq!(("TEXT".into(), "".into()), "TEXT".ansi_split_at(100));
2149 assert_eq!(
2150 ("\u{1b}[30mTEXT\u{1b}[39m".into(), "".into()),
2151 "\u{1b}[30mTEXT\u{1b}[39m".ansi_split_at(100)
2152 );
2153 }
2154
2155 #[test]
2156 #[should_panic]
2157 fn split_of_emojie_test() {
2158 "😀".ansi_split_at(1);
2159 }
2160
2161 #[test]
2162 fn starts_with_test() {
2163 let text = "\u{1b}[30mTEXT\u{1b}[39m";
2164 assert!(text.ansi_starts_with(""));
2165 assert!(text.ansi_starts_with("T"));
2166 assert!(text.ansi_starts_with("TE"));
2167 assert!(text.ansi_starts_with("TEX"));
2168 assert!(text.ansi_starts_with("TEXT"));
2169 assert!(!text.ansi_starts_with("123"));
2170 assert!(!text.ansi_starts_with("TEX+"));
2171 assert!(!text.ansi_starts_with("TEXT NOT STARTED WITH"));
2172 assert!(!text.ansi_starts_with("EXT"));
2173
2174 let texts = [
2175 "\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39m\u{1b}[49m",
2176 "\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2177 ];
2178 for text in texts {
2179 assert!(text.ansi_starts_with(""));
2180 assert!(text.ansi_starts_with("T"));
2181 assert!(text.ansi_starts_with("TE"));
2182 assert!(text.ansi_starts_with("TEX"));
2183 assert!(text.ansi_starts_with("TEXT"));
2184 assert!(text.ansi_starts_with("TEXT "));
2185 assert!(text.ansi_starts_with("TEXT 1"));
2186 assert!(text.ansi_starts_with("TEXT 12"));
2187 assert!(text.ansi_starts_with("TEXT 123"));
2188 assert!(!text.ansi_starts_with("TEXT+"));
2189 assert!(!text.ansi_starts_with("TEXT +"));
2190 assert!(!text.ansi_starts_with("TEXT 12+"));
2191 assert!(!text.ansi_starts_with("TEXT 12NOT THERE"));
2192 assert!(!text.ansi_starts_with("NOT THERE"));
2193 assert!(!text.ansi_starts_with("EXT 123"));
2194 }
2195 }
2196
2197 #[test]
2198 fn starts_with_uses_chars_so_dont_panic_test() {
2199 assert!(!"TE".ansi_starts_with("😀"));
2200 assert!(!"T".ansi_starts_with("Щ"));
2201 }
2202
2203 #[test]
2204 fn ends_with_test() {
2205 let text = "\u{1b}[30mTEXT\u{1b}[39m";
2206 assert!(text.ansi_ends_with(""));
2207 assert!(text.ansi_ends_with("T"));
2208 assert!(text.ansi_ends_with("XT"));
2209 assert!(text.ansi_ends_with("EXT"));
2210 assert!(text.ansi_ends_with("TEXT"));
2211 assert!(!text.ansi_ends_with("123"));
2212 assert!(!text.ansi_ends_with("TEXT NOT STARTED WITH"));
2213 assert!(!text.ansi_ends_with("EXT+"));
2214 assert!(!text.ansi_ends_with("+EXT"));
2215 assert!(!text.ansi_ends_with("TEX"));
2216
2217 let texts = [
2218 "\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39m\u{1b}[49m",
2219 "\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2220 ];
2221 for text in texts {
2222 assert!(text.ansi_ends_with(""));
2223 assert!(text.ansi_ends_with("3"));
2224 assert!(text.ansi_ends_with("23"));
2225 assert!(text.ansi_ends_with("123"));
2226 assert!(text.ansi_ends_with(" 123"));
2227 assert!(text.ansi_ends_with("T 123"));
2228 assert!(text.ansi_ends_with("XT 123"));
2229 assert!(text.ansi_ends_with("EXT 123"));
2230 assert!(text.ansi_ends_with("TEXT 123"));
2231 assert!(!text.ansi_ends_with("123+"));
2232 assert!(!text.ansi_ends_with("+123"));
2233 assert!(!text.ansi_ends_with(" +123"));
2234 assert!(!text.ansi_ends_with("+ 123"));
2235 assert!(!text.ansi_ends_with("TEXT 12NOT THERE"));
2236 assert!(!text.ansi_ends_with("NOT THERE"));
2237 assert!(!text.ansi_ends_with("TEXT 12"));
2238 }
2239 }
2240
2241 #[test]
2242 fn ends_with_uses_chars_so_dont_panic_test() {
2243 assert!(!"TE".ansi_ends_with("😀"));
2244 assert!(!"T".ansi_ends_with("Щ"));
2245 }
2246
2247 #[test]
2248 fn trim_test() {
2249 assert_eq!("", "".ansi_trim());
2250 assert_eq!("", " ".ansi_trim());
2251 assert_eq!("TEXT", "TEXT".ansi_trim());
2252 assert_eq!("TEXT", " TEXT".ansi_trim());
2253 assert_eq!("TEXT", "TEXT ".ansi_trim());
2254 assert_eq!("TEXT", " TEXT ".ansi_trim());
2255
2256 let texts = [
2257 "\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39m\u{1b}[49m",
2258 "\u{1b}[41m\u{1b}[30m TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39m\u{1b}[49m",
2259 "\u{1b}[41m\u{1b}[30m TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39m\u{1b}[49m",
2260 "\u{1b}[41m\u{1b}[30m TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39m\u{1b}[49m",
2261 "\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39m\u{1b}[49m",
2262 "\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39m\u{1b}[49m",
2263 "\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39m\u{1b}[49m",
2264 "\u{1b}[41m\u{1b}[30m TEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39m\u{1b}[49m",
2265 "\u{1b}[41m\u{1b}[30m TEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39m\u{1b}[49m",
2266 "\u{1b}[41m\u{1b}[30m TEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39m\u{1b}[49m",
2267 ];
2268 for text in texts {
2269 assert_eq!(
2270 "\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39m\u{1b}[49m",
2271 text.ansi_trim()
2272 );
2273 }
2274
2275 let texts = [
2276 "\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2277 "\u{1b}[41;30m TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2278 "\u{1b}[41;30m TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2279 "\u{1b}[41;30m TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2280 "\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39;49m",
2281 "\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39;49m",
2282 "\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39;49m",
2283 "\u{1b}[41;30m TEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39;49m",
2284 "\u{1b}[41;30m TEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39;49m",
2285 "\u{1b}[41;30m TEXT\u{1b}[39m \u{1b}[34m123 \u{1b}[39;49m",
2286 ];
2287 for text in texts {
2288 assert_eq!(
2289 "\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2290 text.ansi_trim()
2291 );
2292 }
2293 }
2294
2295 #[test]
2296 fn strip_prefix_test() {
2297 macro_rules! test_prefix {
2298 ($text:expr, $prefix:expr, $expected:expr $(,)? ) => {
2299 assert_eq!(
2300 $expected.map(Cow::Borrowed),
2301 $text.ansi_strip_prefix($prefix),
2302 );
2303 };
2304 }
2305
2306 test_prefix!(
2318 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2319 "qwe:TEXT QWE",
2320 Some("\u{1b}[41m\u{1b}[30m\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m"),
2321 );
2322 test_prefix!(
2323 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2324 "qwe:",
2325 Some("\u{1b}[41m\u{1b}[30mTEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m"),
2326 );
2327 test_prefix!(
2328 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2329 "qwe:TEXT",
2330 Some("\u{1b}[41m\u{1b}[30m\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m"),
2331 );
2332 test_prefix!(
2333 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2334 "qwe:TEXT ",
2335 Some("\u{1b}[41m\u{1b}[30m\u{1b}[39m\u{1b}[34mQWE\u{1b}[39m\u{1b}[49m"),
2336 );
2337 test_prefix!(
2338 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2339 "qwe:TEXT ",
2340 Some("\u{1b}[41m\u{1b}[30m\u{1b}[39m\u{1b}[34mQWE\u{1b}[39m\u{1b}[49m"),
2341 );
2342 test_prefix!(
2343 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2344 "qwe:TEXT ",
2345 Some("\u{1b}[41m\u{1b}[30m\u{1b}[39m\u{1b}[34mQWE\u{1b}[39m\u{1b}[49m"),
2346 );
2347 test_prefix!(
2348 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2349 "qwe:TEXT QW",
2350 Some("\u{1b}[41m\u{1b}[30m\u{1b}[39m\u{1b}[34mE\u{1b}[39m\u{1b}[49m"),
2351 );
2352 test_prefix!(
2353 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2354 "we:",
2355 None,
2356 );
2357 test_prefix!(
2358 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2359 ":",
2360 None,
2361 );
2362 test_prefix!(
2363 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2364 "QWE",
2365 None,
2366 );
2367 test_prefix!(
2368 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2369 "",
2370 Some("\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m"),
2371 );
2372 test_prefix!(
2373 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2374 "qwe:TEXT 123",
2375 Some("\u{1b}[41;30m\u{1b}[39m\u{1b}[34m\u{1b}[39;49m"),
2376 );
2377 test_prefix!(
2378 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2379 "qwe:",
2380 Some("\u{1b}[41;30mTEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m"),
2381 );
2382 test_prefix!(
2383 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2384 "qwe:TEXT",
2385 Some("\u{1b}[41;30m\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m"),
2386 );
2387 test_prefix!(
2388 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2389 "qwe:TEXT ",
2390 Some("\u{1b}[41;30m\u{1b}[39m\u{1b}[34m123\u{1b}[39;49m"),
2391 );
2392 test_prefix!(
2393 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2394 "qwe:TEXT 12",
2395 Some("\u{1b}[41;30m\u{1b}[39m\u{1b}[34m3\u{1b}[39;49m"),
2396 );
2397 test_prefix!(
2398 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2399 "qwe:TEXT 123",
2400 Some("\u{1b}[41;30m\u{1b}[39m\u{1b}[34m\u{1b}[39;49m"),
2401 );
2402 test_prefix!(
2403 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2404 "we:",
2405 None,
2406 );
2407 test_prefix!(
2408 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2409 ":",
2410 None,
2411 );
2412 test_prefix!(
2413 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m",
2414 "QWE",
2415 None,
2416 );
2417 }
2418
2419 #[test]
2420 fn strip_suffix_test() {
2421 let text = "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m";
2432 assert_eq!(None, text.ansi_strip_suffix(text));
2434 assert_eq!(
2435 Some("\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQW\u{1b}[39m\u{1b}[49m".into()),
2436 text.ansi_strip_suffix("E")
2437 );
2438 assert_eq!(
2439 Some("\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQ\u{1b}[39m\u{1b}[49m".into()),
2440 text.ansi_strip_suffix("WE")
2441 );
2442 assert_eq!(
2443 Some("\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2444 text.ansi_strip_suffix("QWE")
2445 );
2446 assert_eq!(
2447 Some("\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2448 text.ansi_strip_suffix(" QWE")
2449 );
2450 assert_eq!(
2451 Some("\u{1b}[41m\u{1b}[30mqwe:TEX\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2452 text.ansi_strip_suffix("T QWE")
2453 );
2454 assert_eq!(
2455 Some("\u{1b}[41m\u{1b}[30mqwe:TE\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2456 text.ansi_strip_suffix("XT QWE")
2457 );
2458 assert_eq!(
2459 Some("\u{1b}[41m\u{1b}[30mqwe:T\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2460 text.ansi_strip_suffix("EXT QWE")
2461 );
2462 assert_eq!(
2463 Some("\u{1b}[41m\u{1b}[30mqwe:\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2464 text.ansi_strip_suffix("TEXT QWE")
2465 );
2466 assert_eq!(
2467 Some("\u{1b}[41m\u{1b}[30mqwe\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2468 text.ansi_strip_suffix(":TEXT QWE")
2469 );
2470 assert_eq!(
2471 Some("\u{1b}[41m\u{1b}[30mqw\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2472 text.ansi_strip_suffix("e:TEXT QWE")
2473 );
2474 assert_eq!(
2475 Some("\u{1b}[41m\u{1b}[30mq\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2476 text.ansi_strip_suffix("we:TEXT QWE")
2477 );
2478 assert_eq!(
2479 Some("\u{1b}[41m\u{1b}[30m\u{1b}[39m\u{1b}[34m\u{1b}[39m\u{1b}[49m".into()),
2480 text.ansi_strip_suffix("qwe:TEXT QWE")
2481 );
2482 assert_eq!(None, text.ansi_strip_suffix("qwe:TEXT QW"));
2483 assert_eq!(None, text.ansi_strip_suffix("qwe:"));
2484 assert_eq!(None, text.ansi_strip_suffix("QW"));
2485
2486 let text = "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m123\u{1b}[39;49m";
2487 assert_eq!(Some(text.into()), text.ansi_strip_suffix(""));
2488 assert_eq!(None, text.ansi_strip_suffix(text));
2489 assert_eq!(
2490 Some("\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m12\u{1b}[39;49m".into()),
2491 text.ansi_strip_suffix("3")
2492 );
2493 assert_eq!(
2494 Some("\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m1\u{1b}[39;49m".into()),
2495 text.ansi_strip_suffix("23")
2496 );
2497 assert_eq!(
2498 Some("\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34m\u{1b}[39;49m".into()),
2499 text.ansi_strip_suffix("123")
2500 );
2501 assert_eq!(
2502 Some("\u{1b}[41;30mqwe:TEXT\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2503 text.ansi_strip_suffix(" 123")
2504 );
2505 assert_eq!(
2506 Some("\u{1b}[41;30mqwe:TEX\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2507 text.ansi_strip_suffix("T 123")
2508 );
2509 assert_eq!(
2510 Some("\u{1b}[41;30mqwe:TE\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2511 text.ansi_strip_suffix("XT 123")
2512 );
2513 assert_eq!(
2514 Some("\u{1b}[41;30mqwe:T\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2515 text.ansi_strip_suffix("EXT 123")
2516 );
2517 assert_eq!(
2518 Some("\u{1b}[41;30mqwe:\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2519 text.ansi_strip_suffix("TEXT 123")
2520 );
2521 assert_eq!(
2522 Some("\u{1b}[41;30mqwe\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2523 text.ansi_strip_suffix(":TEXT 123")
2524 );
2525 assert_eq!(
2526 Some("\u{1b}[41;30mqw\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2527 text.ansi_strip_suffix("e:TEXT 123")
2528 );
2529 assert_eq!(
2530 Some("\u{1b}[41;30mq\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2531 text.ansi_strip_suffix("we:TEXT 123")
2532 );
2533 assert_eq!(
2534 Some("\u{1b}[41;30m\u{1b}[39m\u{1b}[34m\u{1b}[39;49m".into()),
2535 text.ansi_strip_suffix("qwe:TEXT 123")
2536 );
2537 assert_eq!(None, text.ansi_strip_suffix("qwe:TEXT 12"));
2538 assert_eq!(None, text.ansi_strip_suffix("qwe:"));
2539 assert_eq!(None, text.ansi_strip_suffix("2"));
2540 }
2541
2542 #[test]
2543 fn find_test() {
2544 assert_eq!("".find(""), "".ansi_find(""));
2545
2546 let text = "qwe:TEXT";
2547 assert_eq!(Some(0), text.ansi_find("q"));
2548 assert_eq!(Some(0), text.ansi_find("qwe"));
2549 assert_eq!(Some(1), text.ansi_find("we"));
2550 assert_eq!(Some(3), text.ansi_find(":"));
2551 assert_eq!(Some(4), text.ansi_find("TEXT"));
2552
2553 let text = "\u{1b}[30mqwe:TEXT\u{1b}[39m";
2554 assert_eq!(Some(0), text.ansi_find("q"));
2555 assert_eq!(Some(0), text.ansi_find("qwe"));
2556 assert_eq!(Some(1), text.ansi_find("we"));
2557 assert_eq!(Some(3), text.ansi_find(":"));
2558 assert_eq!(Some(4), text.ansi_find("TEXT"));
2559
2560 let text = "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m";
2561 assert_eq!(Some(0), text.ansi_find("q"));
2562 assert_eq!(Some(0), text.ansi_find("qwe"));
2563 assert_eq!(Some(1), text.ansi_find("we"));
2564 assert_eq!(Some(3), text.ansi_find(":"));
2565 assert_eq!(Some(4), text.ansi_find("TEXT"));
2566 assert_eq!(Some(5), text.ansi_find("E"));
2567 assert_eq!(Some(8), text.ansi_find(" "));
2568 assert_eq!(Some(9), text.ansi_find("QWE"));
2569
2570 let text = "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39;49m";
2571 assert_eq!(Some(0), text.ansi_find("q"));
2572 assert_eq!(Some(0), text.ansi_find("qwe"));
2573 assert_eq!(Some(1), text.ansi_find("we"));
2574 assert_eq!(Some(3), text.ansi_find(":"));
2575 assert_eq!(Some(4), text.ansi_find("TEXT"));
2576 assert_eq!(Some(5), text.ansi_find("E"));
2577 assert_eq!(Some(8), text.ansi_find(" "));
2578 assert_eq!(Some(9), text.ansi_find("QWE"));
2579 }
2580
2581 #[test]
2582 fn split_test() {
2583 assert_eq!(
2584 "213".split("").collect::<Vec<_>>(),
2585 "213".ansi_split("").collect::<Vec<_>>()
2586 );
2587 assert_eq!(
2588 "".split("").collect::<Vec<_>>(),
2589 "".ansi_split("").collect::<Vec<_>>()
2590 );
2591
2592 let text = "123:456";
2593 assert_eq!(
2594 text.split(':').collect::<Vec<_>>(),
2595 text.ansi_split(":").collect::<Vec<_>>()
2596 );
2597 assert_eq!(
2598 text.split("").collect::<Vec<_>>(),
2599 text.ansi_split("").collect::<Vec<_>>()
2600 );
2601 assert_eq!(
2602 text.split("TEXT").collect::<Vec<_>>(),
2603 text.ansi_split("TEXT").collect::<Vec<_>>()
2604 );
2605 assert_eq!(
2606 text.split("123").collect::<Vec<_>>(),
2607 text.ansi_split("123").collect::<Vec<_>>()
2608 );
2609 assert_eq!(
2610 text.split("456").collect::<Vec<_>>(),
2611 text.ansi_split("456").collect::<Vec<_>>()
2612 );
2613
2614 let text = "123:456:789";
2615 assert_eq!(
2616 text.split(':').collect::<Vec<_>>(),
2617 text.ansi_split(":").collect::<Vec<_>>()
2618 );
2619 assert_eq!(
2620 text.split("").collect::<Vec<_>>(),
2621 text.ansi_split("").collect::<Vec<_>>()
2622 );
2623 assert_eq!(
2624 text.split("TEXT").collect::<Vec<_>>(),
2625 text.ansi_split("TEXT").collect::<Vec<_>>()
2626 );
2627 assert_eq!(
2628 text.split("123").collect::<Vec<_>>(),
2629 text.ansi_split("123").collect::<Vec<_>>()
2630 );
2631 assert_eq!(
2632 text.split("456").collect::<Vec<_>>(),
2633 text.ansi_split("456").collect::<Vec<_>>()
2634 );
2635 assert_eq!(
2636 text.split("789").collect::<Vec<_>>(),
2637 text.ansi_split("789").collect::<Vec<_>>()
2638 );
2639
2640 assert_eq!(
2641 ":123:456:789".split(':').collect::<Vec<_>>(),
2642 ":123:456:789".ansi_split(":").collect::<Vec<_>>()
2643 );
2644 assert_eq!(
2645 "123:456:789:".split(':').collect::<Vec<_>>(),
2646 "123:456:789:".ansi_split(":").collect::<Vec<_>>()
2647 );
2648 assert_eq!(
2649 ":123:456:789:".split(':').collect::<Vec<_>>(),
2650 ":123:456:789:".ansi_split(":").collect::<Vec<_>>()
2651 );
2652
2653 let text = "\u{1b}[30m123:456\u{1b}[39m";
2654 assert_eq!(
2655 vec!["\u{1b}[30m123\u{1b}[39m", "\u{1b}[30m456\u{1b}[39m"],
2656 text.ansi_split(":").collect::<Vec<_>>()
2657 );
2658 assert_eq!(
2659 vec!["\u{1b}[30m123:\u{1b}[39m", "\u{1b}[30m\u{1b}[39m"],
2660 text.ansi_split("456").collect::<Vec<_>>()
2661 );
2662
2663 let text = "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m";
2664 assert_eq!(
2665 vec![
2666 "\u{1b}[41m\u{1b}[30mqwe\u{1b}[39m\u{1b}[49m",
2667 "\u{1b}[30m\u{1b}[41mTEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m"
2668 ],
2669 text.ansi_split(":").collect::<Vec<_>>()
2670 );
2671 assert_eq!(vec![text], text.ansi_split("456").collect::<Vec<_>>());
2672 assert_eq!(
2673 vec![text.to_owned()],
2674 text.ansi_split("NOT FOUND").collect::<Vec<_>>()
2675 );
2676
2677 let text = "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39;49m";
2678 assert_eq!(
2679 vec![
2680 "\u{1b}[41;30mqwe\u{1b}[39m\u{1b}[49m",
2681 "\u{1b}[30m\u{1b}[41mTEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39;49m"
2682 ],
2683 text.ansi_split(":").collect::<Vec<_>>()
2684 );
2685 assert_eq!(
2686 vec!["\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39;49m"],
2687 text.ansi_split("456").collect::<Vec<_>>()
2688 );
2689 assert_eq!(
2690 vec![text.to_owned()],
2691 text.ansi_split("NOT FOUND").collect::<Vec<_>>()
2692 );
2693
2694 assert_eq!(
2695 "\u{1b}[31mlionXXtigerXleopard\u{1b}[39m"
2696 .ansi_split("X")
2697 .collect::<Vec<_>>(),
2698 [
2699 "\u{1b}[31mlion\u{1b}[39m",
2700 "",
2701 "\u{1b}[31mtiger\u{1b}[39m",
2702 "\u{1b}[31mleopard\u{1b}[39m"
2703 ],
2704 );
2705
2706 }
2715
2716 #[test]
2717 fn split_at_color_preservation_test() {
2718 assert_eq!(
2726 "\u{1b}[38;5;12mTEXT\u{1b}[39m".ansi_split_at(2),
2727 (
2728 "\u{1b}[38;5;12mTE\u{1b}[39m".into(),
2729 "\u{1b}[38;5;12mXT\u{1b}[39m".into()
2730 ),
2731 );
2732 assert_eq!(
2733 "\u{1b}[38;2;100;123;1mTEXT\u{1b}[39m".ansi_split_at(2),
2734 (
2735 "\u{1b}[38;2;100;123;1mTE\u{1b}[39m".into(),
2736 "\u{1b}[38;2;100;123;1mXT\u{1b}[39m".into()
2737 ),
2738 );
2739 assert_eq!(
2740 "\u{1b}[38;5;30mTEXT\u{1b}[39m".ansi_split_at(2),
2741 (
2742 "\u{1b}[38;5;30mTE\u{1b}[39m".into(),
2743 "\u{1b}[38;5;30mXT\u{1b}[39m".into()
2744 ),
2745 );
2746 assert_eq!(
2747 "\u{1b}[48;2;023;011;100m\u{1b}[31mHello\u{1b}[39m\u{1b}[49m \u{1b}[32;43mWorld\u{1b}[0m".ansi_split_at(6),
2748 ("\u{1b}[31m\u{1b}[48;2;23;11;100mHello\u{1b}[39m\u{1b}[49m ".into(), "\u{1b}[32m\u{1b}[43mWorld\u{1b}[39m\u{1b}[49m".into()),
2749 );
2750 }
2751
2752 #[test]
2753 fn get_blocks_test() {
2754 macro_rules! test_blocks {
2755 ([$($string:expr),* $(,)?], $expected:expr) => {
2756 $(
2757 assert_eq!(
2758 get_blocks($string).collect::<Vec<_>>(),
2759 $expected,
2760 );
2761 )*
2762 };
2763 }
2764
2765 test_blocks!([""], []);
2766
2767 test_blocks!(
2768 ["213"],
2769 [AnsiBlock::new(Cow::Borrowed("213"), AnsiState::default())]
2770 );
2771
2772 test_blocks!(
2773 ["213\n456"],
2774 [AnsiBlock::new(
2775 Cow::Borrowed("213\n456"),
2776 AnsiState::default()
2777 )]
2778 );
2779
2780 test_blocks!(
2781 [
2782 "\u{1b}[30m123:456\u{1b}[39m",
2783 "\u{1b}[30m123:456\u{1b}[0m",
2784 "\u{1b}[30m123:456",
2785 ],
2786 [AnsiBlock::new(
2787 Cow::Borrowed("123:456"),
2788 AnsiState {
2789 fg_color: Some(AnsiColor::Bit4(30)),
2790 ..Default::default()
2791 }
2792 )]
2793 );
2794
2795 test_blocks!(
2796 [
2797 "\u{1b}[30m123\n:\n456\u{1b}[39m",
2798 "\u{1b}[30m123\n:\n456\u{1b}[0m",
2799 "\u{1b}[30m123\n:\n456",
2800 ],
2801 [AnsiBlock::new(
2802 Cow::Borrowed("123\n:\n456"),
2803 AnsiState {
2804 fg_color: Some(AnsiColor::Bit4(30)),
2805 ..Default::default()
2806 }
2807 )]
2808 );
2809
2810 test_blocks!(
2811 [
2812 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2813 "\u{1b}[41;30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39;49m",
2814 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[0m",
2815 "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE",
2816 ],
2817 [
2818 AnsiBlock::new(
2819 Cow::Borrowed("qwe:TEXT"),
2820 AnsiState {
2821 fg_color: Some(AnsiColor::Bit4(30)),
2822 bg_color: Some(AnsiColor::Bit4(41)),
2823 ..Default::default()
2824 }
2825 ),
2826 AnsiBlock::new(
2827 Cow::Borrowed(" "),
2828 AnsiState {
2829 bg_color: Some(AnsiColor::Bit4(41)),
2830 ..Default::default()
2831 }
2832 ),
2833 AnsiBlock::new(
2834 Cow::Borrowed("QWE"),
2835 AnsiState {
2836 fg_color: Some(AnsiColor::Bit4(34)),
2837 bg_color: Some(AnsiColor::Bit4(41)),
2838 ..Default::default()
2839 }
2840 ),
2841 ]
2842 );
2843
2844 test_blocks!(
2845 ["\u{1b}[31mlionXXtigerXleopard\u{1b}[39m"],
2846 [AnsiBlock::new(
2847 Cow::Borrowed("lionXXtigerXleopard"),
2848 AnsiState {
2849 fg_color: Some(AnsiColor::Bit4(31)),
2850 ..Default::default()
2851 },
2852 )]
2853 );
2854
2855 test_blocks!(
2856 ["\u{1b}[41;30m Hello \u{1b}[0m \t \u{1b}[43;32m World \u{1b}[0m",],
2857 [
2858 AnsiBlock::new(
2859 Cow::Borrowed(" Hello "),
2860 AnsiState {
2861 fg_color: Some(AnsiColor::Bit4(30)),
2862 bg_color: Some(AnsiColor::Bit4(41)),
2863 ..Default::default()
2864 }
2865 ),
2866 AnsiBlock::new(
2867 Cow::Borrowed(" \t "),
2868 AnsiState {
2869 reset: true,
2870 ..Default::default()
2871 },
2872 ),
2873 AnsiBlock::new(
2874 Cow::Borrowed(" World "),
2875 AnsiState {
2876 fg_color: Some(AnsiColor::Bit4(32)),
2877 bg_color: Some(AnsiColor::Bit4(43)),
2878 reset: true,
2879 ..Default::default()
2880 },
2881 ),
2882 ]
2883 );
2884
2885 test_blocks!(
2886 ["\u{1b}[41;30m Hello \t \u{1b}[43;32m World \u{1b}[0m",],
2887 [
2888 AnsiBlock::new(
2889 Cow::Borrowed(" Hello \t "),
2890 AnsiState {
2891 fg_color: Some(AnsiColor::Bit4(30)),
2892 bg_color: Some(AnsiColor::Bit4(41)),
2893 ..Default::default()
2894 }
2895 ),
2896 AnsiBlock::new(
2897 Cow::Borrowed(" World "),
2898 AnsiState {
2899 fg_color: Some(AnsiColor::Bit4(32)),
2900 bg_color: Some(AnsiColor::Bit4(43)),
2901 ..Default::default()
2902 },
2903 ),
2904 ]
2905 );
2906 }
2907
2908 #[test]
2909 fn font_usage_test() {
2910 assert_eq!(
2911 "\u{1b}[12mTEXT\u{1b}[10m".ansi_split_at(2),
2912 (
2913 "\u{1b}[12mTE\u{1b}[10m".into(),
2914 "\u{1b}[12mXT\u{1b}[10m".into()
2915 ),
2916 );
2917 }
2918
2919 #[test]
2920 fn ansi_split2_test() {
2921 let a = "\u{1b}[2;48;5;10m\u{1b}[38;5;20mDar\nren\u{1b}[0m"
2922 .ansi_split("\n")
2923 .collect::<Vec<_>>();
2924 assert_eq!(
2925 a,
2926 [
2927 "\u{1b}[2;48;5;10m\u{1b}[38;5;20mDar\u{1b}[22m\u{1b}[39m\u{1b}[49m",
2928 "\u{1b}[2m\u{1b}[38;5;20m\u{1b}[48;5;10mren\u{1b}[0m"
2929 ]
2930 );
2931 }
2932
2933 #[test]
2934 fn ansi_split3_test_reverse() {
2935 let a = "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[7;34marg\u{1b}[0m\u{1b}[37muments.\u{1b}[0m"
2936 .ansi_split("g")
2937 .collect::<Vec<_>>();
2938 assert_eq!(
2939 a,
2940 [
2941 "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[7;34mar\u{1b}[27m\u{1b}[39m",
2942 "\u{1b}[7m\u{1b}[34m\u{1b}[0m\u{1b}[37muments.\u{1b}[0m"
2943 ]
2944 );
2945 }
2946
2947 #[test]
2948 fn ansi_split4_test_hide() {
2949 let a = "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[8;34marg\u{1b}[0m\u{1b}[37muments.\u{1b}[0m"
2950 .ansi_split("g")
2951 .collect::<Vec<_>>();
2952 assert_eq!(
2953 a,
2954 [
2955 "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[8;34mar\u{1b}[28m\u{1b}[39m",
2956 "\u{1b}[8m\u{1b}[34m\u{1b}[0m\u{1b}[37muments.\u{1b}[0m"
2957 ]
2958 );
2959 }
2960}