ansi_str/
lib.rs

1#![allow(clippy::uninlined_format_args)]
2
3//! # `ansi_str`
4//!
5//! A library which provides a set of methods to work with strings escaped with ansi sequences.
6//!
7//! It's an agnostic library in regard to different color libraries.
8//! Therefore it can be used with any library (e.g. [owo-colors](https://crates.io/crates/owo-colors),
9//! [nu-ansi-term](https://crates.io/crates/nu-ansi-term)).
10//!
11//! # Example
12//!
13//! ```
14//! use ansi_str::AnsiStr;
15//!
16//! let text = String::from("\u{1b}[31mHello World!\u{1b}[39m");
17//! let (hello, world) = text.ansi_split_at(6);
18//!
19//! println!("{}", hello);
20//! println!("{}", world);
21//! ```
22//!
23//! ## Note
24//!
25//! The library doesn't guarantee to keep style of usage of ansi sequences.
26//!  
27//! For example if your string is `"\u{1b}[31;40mTEXT\u{1b}[0m"` and you will call get method.
28//! It may not use `"\u{1b}[31;40m"` but it use it as `"\u{1b}[31m"` and `"\u{1b}[40m"`.
29//!
30//! Why that matters is because for example the following code example is not guaranteed to be true.
31//!
32//! ```,ignore
33//! use ansi_str::AnsiStr;
34//!
35//! pub fn main() {
36//!     let text = "\u{1b}[31mHello World!\u{1b}[0m";
37//!     let text1 = hello1.ansi_get(..).unwrap();
38//!     assert_eq!(text, text1)
39//! }
40//! ```
41
42// todo: refactoring to use an iterator over chars and it hold a state for each of the chars?
43// todo: Maybe it's worth to create some type like AnsiString which would not necessarily allocate String underthehood
44// todo: Quickcheck tests
45
46#![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
54/// [`AnsiStr`] represents a list of functions to work with colored strings
55/// defined as ANSI control sequences.
56pub trait AnsiStr {
57    /// Returns a substring of a string.
58    ///
59    /// It preserves accurate style of a substring.
60    ///
61    /// Range is defined in terms of `byte`s of the string not containing ANSI control sequences
62    /// (If the string is stripped).
63    ///
64    /// This is the non-panicking alternative to `[Self::ansi_cut]`.
65    /// Returns `None` whenever equivalent indexing operation would panic.
66    ///
67    /// Exceeding the boundaries of the string results in the
68    /// same result if the upper boundary to be equal to the string length.
69    ///
70    /// If the text doesn't contains any ansi sequences the function must return result  if `[str::get]` was called.  
71    ///
72    /// # Examples
73    ///
74    /// Basic usage:
75    ///
76    /// ```
77    /// use ansi_str::AnsiStr;
78    ///
79    /// let text = "\u{1b}[31m🗻 on the 🌏\u{1b}[39m";
80    ///
81    /// assert_eq!(text.ansi_get(0..7), Some("\u{1b}[31m🗻 on\u{1b}[39m".into()));
82    ///
83    /// // indices not on UTF-8 sequence boundaries
84    /// assert!(text.ansi_get(1..).is_none());
85    /// assert!(text.ansi_get(..13).is_none());
86    ///
87    /// // going over boundries doesn't panic
88    /// assert!(text.ansi_get(..std::usize::MAX).is_some());
89    /// assert!(text.ansi_get(std::usize::MAX..).is_some());
90    /// ```
91    ///
92    /// Text doesn't contain ansi sequences
93    ///
94    /// ```
95    /// use ansi_str::AnsiStr;
96    ///
97    /// let text = "🗻 on the 🌏";
98    ///
99    /// assert_eq!(text.ansi_get(5..), Some("on the 🌏".into()));
100    /// ```
101    fn ansi_get<I>(&self, i: I) -> Option<Cow<'_, str>>
102    where
103        I: RangeBounds<usize>;
104
105    /// Cut makes a sub string, keeping the colors in the substring.
106    ///
107    /// The ANSI escape sequences are ignored when calculating the positions within the string.
108    ///
109    /// Range is defined in terms of `byte`s of the string not containing ANSI control sequences
110    /// (If the string is stripped).
111    ///
112    /// Exceeding an upper bound does not panic.
113    ///
114    /// # Panics
115    ///
116    /// Panics if a start or end indexes are not on a UTF-8 code point boundary.
117    ///
118    /// # Examples
119    ///
120    /// Basic usage:
121    ///
122    /// ```
123    /// use ansi_str::AnsiStr;
124    ///
125    /// let text = "\u{1b}[31;40m🗻 on the 🌏\u{1b}[0m";
126    /// assert_eq!(text.ansi_cut(0..4).ansi_strip(), "🗻");
127    /// assert_eq!(text.ansi_cut(..7).ansi_strip(), "🗻 on");
128    /// assert_eq!(text.ansi_cut(8..).ansi_strip(), "the 🌏");
129    /// ```
130    ///
131    /// Panics when index is not a valud UTF-8 char
132    ///
133    /// ```should_panic
134    /// use ansi_str::AnsiStr;
135    ///
136    /// let text = "\u{1b}[31;40m🗻 on the 🌏\u{1b}[0m";
137    /// text.ansi_cut(1..);
138    /// ```
139    fn ansi_cut<I>(&self, i: I) -> Cow<'_, str>
140    where
141        I: RangeBounds<usize>;
142
143    /// Checks that index-th byte is the first byte in a UTF-8 code point sequence or the end of the string.
144    ///
145    /// The index is determined in a string if it would be stripped.
146    ///
147    /// # Examples
148    ///
149    /// Basic usage:
150    ///
151    /// ```
152    /// use ansi_str::AnsiStr;
153    ///
154    /// let text = "\u{1b}[34mLöwe 老虎 Léopard\u{1b}[39m";
155    ///
156    /// assert!(text.ansi_is_char_boundary(0));
157    /// // start of `老`
158    /// assert!(text.ansi_is_char_boundary(6));
159    /// assert!(text.ansi_is_char_boundary(text.ansi_strip().len()));
160    ///
161    /// // second byte of `ö`
162    /// assert!(!text.ansi_is_char_boundary(2));
163    ///
164    /// // third byte of `老`
165    /// assert!(!text.ansi_is_char_boundary(8));
166    /// ```
167    fn ansi_is_char_boundary(&self, index: usize) -> bool;
168
169    /// Returns the byte index of the first character of this string slice that matches the pattern,
170    /// considering the ansi sequences.
171    ///
172    /// Returns None if the pattern doesn’t match.
173    ///
174    /// # Examples
175    ///
176    /// Basic usage:
177    ///
178    /// ```
179    /// use ansi_str::AnsiStr;
180    ///
181    /// let text = "\u{1b}[31;40mLöwe 老虎 Léopard Gepardi\u{1b}[0m";
182    /// assert_eq!(text.ansi_find("L"), Some(0));
183    /// assert_eq!(text.ansi_find("é"), Some(14));
184    /// assert_eq!(text.ansi_find("pard"), Some(17));
185    /// ```
186    fn ansi_find(&self, pat: &str) -> Option<usize>;
187
188    /// Returns a string with the prefix removed,
189    /// considering the ansi sequences.
190    ///
191    /// If the string starts with the pattern prefix, returns substring after the prefix, wrapped in Some.
192    ///
193    /// If the string does not start with prefix, returns None.
194    ///
195    /// # Examples
196    ///
197    /// Basic usage:
198    ///
199    /// ```
200    /// use ansi_str::AnsiStr;
201    ///
202    /// let text = "\u{1b}[31mfoo:bar\u{1b}[0m";
203    /// assert_eq!(
204    ///     text.ansi_strip_prefix("foo"),
205    ///     Some("\u{1b}[31m:bar\u{1b}[0m".into()),
206    /// );
207    /// assert_eq!(
208    ///     text.ansi_strip_prefix("bar"),
209    ///     None,
210    /// );
211    /// ```
212    fn ansi_strip_prefix(&self, prefix: &str) -> Option<Cow<'_, str>>;
213
214    /// Returns a string slice with the suffix removed,
215    /// considering the ansi sequences.
216    ///
217    /// If the string ends with the pattern suffix, returns the substring before the suffix, wrapped in Some.
218    ///
219    /// If the string does not end with suffix, returns None.
220    ///
221    /// # Examples
222    ///
223    /// Basic usage:
224    ///
225    /// ```
226    /// use ansi_str::AnsiStr;
227    ///
228    /// let text = "\u{1b}[31mfoo:bar\u{1b}[0m";
229    /// assert_eq!(text.ansi_strip_suffix("bar"), Some("\u{1b}[31mfoo:\u{1b}[0m".into()));
230    /// assert_eq!(text.ansi_strip_suffix("foo"), None);
231    /// ```
232    fn ansi_strip_suffix(&self, pat: &str) -> Option<Cow<'_, str>>;
233
234    /// An iterator over substrings of the string, separated by characters matched by a pattern.
235    /// While keeping colors in substrings.
236    ///
237    /// # Examples
238    ///
239    /// Basic usage:
240    ///
241    /// ```
242    /// use ansi_str::AnsiStr;
243    ///
244    /// let text = "\u{1b}[31mMary had a little lamb\u{1b}[0m";
245    ///
246    /// let words: Vec<_> = text.ansi_split(" ").collect();
247    ///
248    /// assert_eq!(
249    ///     words,
250    ///     [
251    ///         "\u{1b}[31mMary\u{1b}[39m",
252    ///         "\u{1b}[31mhad\u{1b}[39m",
253    ///         "\u{1b}[31ma\u{1b}[39m",
254    ///         "\u{1b}[31mlittle\u{1b}[39m",
255    ///         "\u{1b}[31mlamb\u{1b}[0m",
256    ///     ]
257    /// );
258    ///
259    /// let words: Vec<_> = "".ansi_split("X").collect();
260    /// assert_eq!(words, [""]);
261    ///
262    /// let text = "\u{1b}[31mlionXXtigerXleopard\u{1b}[0m";
263    /// let words: Vec<_> = text.ansi_split("X").collect();
264    /// assert_eq!(words, ["\u{1b}[31mlion\u{1b}[39m", "", "\u{1b}[31mtiger\u{1b}[39m", "\u{1b}[31mleopard\u{1b}[0m"]);
265    ///
266    /// let text = "\u{1b}[31mlion::tiger::leopard\u{1b}[0m";
267    /// let words: Vec<_> = text.ansi_split("::").collect();
268    /// assert_eq!(words, ["\u{1b}[31mlion\u{1b}[39m", "\u{1b}[31mtiger\u{1b}[39m", "\u{1b}[31mleopard\u{1b}[0m"]);
269    /// ```
270    fn ansi_split<'a>(&'a self, pat: &'a str) -> AnsiSplit<'a>;
271
272    /// Divide one string into two at an index.
273    /// While considering colors.
274    ///
275    /// The argument, mid, should be a byte offset from the start of the string.
276    /// It must also be on the boundary of a UTF-8 code point.
277    ///
278    /// The two strings returned go from the start of the string to mid, and from mid to the end of the string.
279    ///
280    /// # Panics
281    ///
282    /// It might panic in case mid is not on the boundry of a UTF-8 code point.
283    ///
284    /// # Examples
285    ///
286    /// Basic usage:
287    ///
288    /// ```
289    /// use ansi_str::AnsiStr;
290    ///
291    /// let text = "\u{1b}[31;40mPer Martin-Löf\u{1b}[0m";
292    ///
293    /// let (first, last) = text.ansi_split_at(3);
294    ///
295    /// assert_eq!(first.ansi_strip(), "Per");
296    /// assert_eq!(last.ansi_strip(), " Martin-Löf");
297    /// ```
298    ///
299    /// Panic
300    ///
301    /// ```should_panic
302    /// use ansi_str::AnsiStr;
303    ///
304    /// let text = "\u{1b}[31;40mPer Martin-Löf\u{1b}[0m";
305    ///
306    /// text.ansi_split_at(13);
307    /// ```
308    fn ansi_split_at(&self, mid: usize) -> (Cow<'_, str>, Cow<'_, str>);
309
310    /// Returns true if the given pattern matches a prefix of this string slice.
311    /// Ignoring the ansi sequences.
312    ///
313    /// Returns false if it does not.
314    ///
315    /// # Examples
316    ///
317    /// Basic usage:
318    ///
319    /// ```
320    /// use ansi_str::AnsiStr;
321    ///
322    /// let text = "\u{1b}[31;40mbananas\u{1b}[0m";
323    ///
324    /// assert!(text.ansi_starts_with("bana"));
325    /// assert!(!text.ansi_starts_with("nana"));
326    /// ```
327    fn ansi_starts_with(&self, pat: &str) -> bool;
328
329    /// Returns true if the given pattern matches a suffix of this string slice.
330    /// Ignoring the ansi sequences.
331    ///
332    /// Returns false if it does not.
333    ///
334    /// # Examples
335    ///
336    /// Basic usage:
337    ///
338    /// ```
339    /// use ansi_str::AnsiStr;
340    ///
341    /// let text = "\u{1b}[31;40mbananas\u{1b}[0m";
342    ///
343    /// assert!(text.ansi_ends_with("anas"));
344    /// assert!(!text.ansi_ends_with("nana"));
345    /// ```
346    fn ansi_ends_with(&self, pat: &str) -> bool;
347
348    /// Returns a string slice with leading and trailing whitespace removed.
349    /// Ignoring the ansi sequences.
350    ///
351    /// # Examples
352    ///
353    /// Basic usage:
354    ///
355    /// ```
356    /// use ansi_str::AnsiStr;
357    ///
358    /// let text = String::from("\u{1b}[31m Hello\tworld\t\u{1b}[39m");
359    ///
360    /// assert_eq!(text.ansi_trim(), "\u{1b}[31mHello\tworld\u{1b}[39m");
361    /// ```
362    fn ansi_trim(&self) -> Cow<'_, str>;
363
364    /// Returns a string with all ANSI sequences removed.
365    ///
366    /// # Examples
367    ///
368    /// Basic usage:
369    ///
370    /// ```
371    /// use ansi_str::AnsiStr;
372    ///
373    /// let text = "\u{1b}[31;40mHello World!\u{1b}[0m";
374    ///
375    /// assert_eq!(text.ansi_strip(), "Hello World!");
376    /// ```
377    fn ansi_strip(&self) -> Cow<'_, str>;
378
379    /// Returns true if a string contains any ansi sequences.
380    ///
381    /// Returns false if it does not.
382    ///
383    /// # Examples
384    ///
385    /// Basic usage:
386    ///
387    /// ```
388    /// use ansi_str::AnsiStr;
389    ///
390    /// assert!(!"Hi".ansi_has_any());
391    /// assert!("\u{1b}[31;40mHi\u{1b}[0m".ansi_has_any());
392    /// ```
393    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    // we check if there's no ansi sequences, and the prefix in the first token
734    // in which case we can return Borrow
735    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                // because it's a first token we can match the whole string
741
742                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                // its safe to use index because we already checked the split point
777                pat = &pat[tkn.len()..];
778            }
779            // fixme: All of this include \u{0x} which must be stripped
780            _ => 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    // we check if there's no ansi sequences, and the prefix in the first token
802    // in which case we can return Borrow
803
804    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                // because it's a first token we can match the whole string
810
811                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            // its safe to use index because we already checked the split point
821            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                // its safe to use index because we already checked the split point
849                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        // We take all the text here so nothing is dropped
874        match pat.get(..text.len()) {
875            Some(p) => {
876                if !text.starts_with(p) {
877                    return false;
878                }
879
880                // its safe to use index because we already checked the split point
881                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    // Use strip because the manual implementaion would not be much faster
897    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    // probably we could check the lengh difference and reuse buf string
936    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    // Can we improve the algorithm?
947    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        // doing small optimization in regard of string with no ansi sequences
966        // which will contain only 1 block of text.
967
968        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
1007/// An [`Iterator`] over matches.
1008/// Created with the method [`AnsiStr::ansi_split`].
1009pub 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/// This function returns a [Iterator] which produces a [`AnsiBlock`].
1059///
1060/// [`AnsiBlock`] represents a string with a consisten style.
1061///
1062/// # Example
1063///
1064/// ```
1065/// use ansi_str::get_blocks;
1066///
1067/// let text = "\u{1b}[31;40mHello\u{1b}[0m \u{1b}[31mWorld!\u{1b}[39m";
1068///
1069/// for block in get_blocks(&text) {
1070///     println!("{:?}", block.text());
1071/// }
1072/// ```
1073#[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
1083/// An [`Iterator`] which produces a [`AnsiBlock`].
1084/// It's created from [`get_blocks`] function.
1085pub 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                    // todo: fix the clone to borrowing.
1102                    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/// An structure which represents a text and it's grafic settings.
1134#[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    /// Text returns a text which is used in the [`AnsiBlock`].
1149    pub fn text(&self) -> &str {
1150        self.text.as_ref()
1151    }
1152
1153    /// The function checks wheather any grafic sequences are set in the [`AnsiBlock`].
1154    pub fn has_ansi(&self) -> bool {
1155        self.state.0.has_any()
1156    }
1157
1158    /// Get a style representation
1159    pub fn style(&self) -> &Style {
1160        &self.state
1161    }
1162}
1163
1164/// An object which can be used to produce a ansi sequences which sets the grafic mode,
1165/// through the [`std::fmt::Display`].
1166pub 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
1178/// An object which can be used to produce a ansi sequences which ends the grafic mode,
1179/// through the [`std::fmt::Display`].
1180pub struct AnsiSequenceEnd<'a>(&'a AnsiState);
1181
1182impl AnsiSequenceEnd<'_> {
1183    /// 'ESC[0m' sequence which can be used in any case.
1184    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/// A style is a structure which contains a flags about a ANSI styles where set.
1198#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1199pub struct Style(AnsiState);
1200
1201impl Style {
1202    /// Returns a [`AnsiSequenceStart`] object which can be used to produce a ansi sequences which sets the grafic mode.
1203    #[must_use]
1204    pub fn start(&self) -> AnsiSequenceStart<'_> {
1205        AnsiSequenceStart(&self.0)
1206    }
1207
1208    /// Returns a [`AnsiSequenceEnd`] object which can be used to produce a ansi sequences which ends the grafic mode.
1209    #[must_use]
1210    pub fn end(&self) -> AnsiSequenceEnd<'_> {
1211        AnsiSequenceEnd(&self.0)
1212    }
1213
1214    /// Returns a foreground color if any was used.
1215    pub fn foreground(&self) -> Option<Color> {
1216        self.0.fg_color.map(Color::from)
1217    }
1218
1219    /// Returns a background color if any was used.
1220    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        /// Check whether a
1228        #[doc = stringify!($name)]
1229        /// is set
1230        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/// A color is one specific type of ANSI escape code, and can refer
1252/// to either the foreground or background color.
1253///
1254/// These use the standard numeric sequences.
1255/// See <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
1256#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1257pub enum Color {
1258    /// Color #0 (foreground code `30`, background code `40`).
1259    ///
1260    /// This is not necessarily the background color, and using it as one may
1261    /// render the text hard to read on terminals with dark backgrounds.
1262    Black,
1263
1264    /// Color #0 (foreground code `90`, background code `100`).
1265    BrightBlack,
1266
1267    /// Color #1 (foreground code `31`, background code `41`).
1268    Red,
1269
1270    /// Color #1 (foreground code `91`, background code `101`).
1271    BrightRed,
1272
1273    /// Color #2 (foreground code `32`, background code `42`).
1274    Green,
1275
1276    /// Color #2 (foreground code `92`, background code `102`).
1277    BrightGreen,
1278
1279    /// Color #3 (foreground code `33`, background code `43`).
1280    Yellow,
1281
1282    /// Color #3 (foreground code `93`, background code `103`).
1283    BrightYellow,
1284
1285    /// Color #4 (foreground code `34`, background code `44`).
1286    Blue,
1287
1288    /// Color #4 (foreground code `94`, background code `104`).
1289    BrightBlue,
1290
1291    /// Color #5 (foreground code `35`, background code `45`).
1292    Purple,
1293
1294    /// Color #5 (foreground code `95`, background code `105`).
1295    BrightPurple,
1296
1297    /// Color #5 (foreground code `35`, background code `45`).
1298    Magenta,
1299
1300    /// Color #5 (foreground code `95`, background code `105`).
1301    BrightMagenta,
1302
1303    /// Color #6 (foreground code `36`, background code `46`).
1304    Cyan,
1305
1306    /// Color #6 (foreground code `96`, background code `106`).
1307    BrightCyan,
1308
1309    /// Color #7 (foreground code `37`, background code `47`).
1310    ///
1311    /// As above, this is not necessarily the foreground color, and may be
1312    /// hard to read on terminals with light backgrounds.
1313    White,
1314
1315    /// Color #7 (foreground code `97`, background code `107`).
1316    BrightWhite,
1317
1318    /// A color number from 0 to 255, for use in 256-color terminal
1319    /// environments.
1320    ///
1321    /// - colors 0 to 7 are the `Black` to `White` variants respectively.
1322    ///   These colors can usually be changed in the terminal emulator.
1323    /// - colors 8 to 15 are brighter versions of the eight colors above.
1324    ///   These can also usually be changed in the terminal emulator, or it
1325    ///   could be configured to use the original colors and show the text in
1326    ///   bold instead. It varies depending on the program.
1327    /// - colors 16 to 231 contain several palettes of bright colors,
1328    ///   arranged in six squares measuring six by six each.
1329    /// - colors 232 to 255 are shades of grey from black to white.
1330    ///
1331    /// It might make more sense to look at a [color chart][cc].
1332    ///
1333    /// [cc]: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
1334    Fixed(u8),
1335
1336    /// A 24-bit Rgb color, as specified by ISO-8613-3.
1337    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                // must never happen
1440                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        // todo: uncomment when parsing ready
1626        // The comment is left as an example that we could combine things by ';'.
1627        //
1628        // macro_rules! __emit {
1629        //     ($foo:expr, $was_written:expr) => {
1630        //         if $was_written {
1631        //             $f.write_str(";")?;
1632        //         } else {
1633        //             $f.write_str("\u{1b}[")?;
1634        //             $was_written = true;
1635        //         }
1636        //
1637        //         $foo?;
1638        //     };
1639        // }
1640        //
1641        // let mut was_written = false;
1642        //
1643        // macro_rules! emit {
1644        //     ($foo:expr) => {
1645        //         __emit!($foo, was_written)
1646        //     };
1647        // }
1648        //
1649        // $b
1650        //
1651        // if was_written {
1652        //     $f.write_char('m')?;
1653        // }
1654
1655        #[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]
1801    // fn parse_ansi_color_test() {
1802    //     let tests: Vec<(&[u8], _)> = vec![
1803    //         (&[5, 200], Some(AnsiColor::Bit8(200))),
1804    //         (&[5, 100, 123, 39], Some(AnsiColor::Bit8(100))),
1805    //         (&[5, 100, 1, 2, 3], Some(AnsiColor::Bit8(100))),
1806    //         (&[5, 1, 2, 3], Some(AnsiColor::Bit8(1))),
1807    //         (&[5], None),
1808    //         (
1809    //             &[2, 100, 123, 39],
1810    //             Some(AnsiColor::Bit24 {
1811    //                 r: 100,
1812    //                 g: 123,
1813    //                 b: 39,
1814    //             }),
1815    //         ),
1816    //         (
1817    //             &[2, 100, 123, 39, 1, 2, 3],
1818    //             Some(AnsiColor::Bit24 {
1819    //                 r: 100,
1820    //                 g: 123,
1821    //                 b: 39,
1822    //             }),
1823    //         ),
1824    //         (
1825    //             &[2, 100, 123, 39, 1, 2, 3],
1826    //             Some(AnsiColor::Bit24 {
1827    //                 r: 100,
1828    //                 g: 123,
1829    //                 b: 39,
1830    //             }),
1831    //         ),
1832    //         (&[2, 100, 123], None),
1833    //         (&[2, 100], None),
1834    //         (&[2], None),
1835    //         (&[], None),
1836    //     ];
1837
1838    //     for (i, (bytes, expected)) in tests.into_iter().enumerate() {
1839    //         assert_eq!(parse_ansi_color(bytes).map(|a| a.0), expected, "test={}", i);
1840    //     }
1841    // }
1842
1843    #[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        // todo: determine if this is the right behaviour
1889        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    // todo: We probably need to fix it.
2034    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!("", "", Some(""));
2307        // test_prefix!("qwe:TEXT", "", Some("qwe:TEXT"));
2308        // test_prefix!("qwe:TEXT", "qwe:TEXT", Some(""));
2309        // test_prefix!("qwe:TEXT", "qwe:", Some("TEXT"));
2310        // test_prefix!("qwe:TEXT", "we:", None);
2311        // test_prefix!("qwe:TEXT", "T", None);
2312        // test_prefix!(
2313        //     "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m",
2314        //     "",
2315        //     Some("\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m"),
2316        // );
2317        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        // assert_eq!(Some("".into()), "".ansi_strip_suffix(""));
2422
2423        // let text = "qwe:TEXT";
2424        // assert_eq!(Some(text.into()), text.ansi_strip_suffix(""));
2425        // assert_eq!(Some("".into()), text.ansi_strip_suffix(text));
2426        // assert_eq!(Some("qwe:TEX".into()), text.ansi_strip_suffix("T"));
2427        // assert_eq!(Some("qwe".into()), text.ansi_strip_suffix(":TEXT"));
2428        // assert_eq!(None, text.ansi_strip_suffix("qwe:"));
2429        // assert_eq!(None, text.ansi_strip_suffix(":"));
2430
2431        let text = "\u{1b}[41m\u{1b}[30mqwe:TEXT\u{1b}[39m \u{1b}[34mQWE\u{1b}[39m\u{1b}[49m";
2432        // assert_eq!(Some(text.into()), text.ansi_strip_suffix(""));
2433        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        // assert_eq!(
2707        //     "\u{1b}[2;48;5;10m\u{1b}[38;5;20mDar\nren\u{1b}[0m"
2708        //         .ansi_split("\n")
2709        //         .collect::<Vec<_>>(),
2710        //     [
2711        //         "\u{1b}[2;48;5;127m\u{1b}[318;5;20mDar\u{1b}[39m", "\u{1b}[38;5;20mren\u{1b}[0m"
2712        //     ],
2713        // )
2714    }
2715
2716    #[test]
2717    fn split_at_color_preservation_test() {
2718        // assert_eq!(
2719        //     "\u{1b}[30mTEXT\u{1b}[39m".ansi_split_at(2),
2720        //     (
2721        //         "\u{1b}[30mTE\u{1b}[39m".into(),
2722        //         "\u{1b}[30mXT\u{1b}[39m".into()
2723        //     ),
2724        // );
2725        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}