// SPDX-License-Identifier: GPL-3.0-only // SPDX-FileCopyrightText: 2023 Denis Drakhnia use std::borrow::Cow; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Color { Black, Red, Green, Yellow, Blue, Cyan, Magenta, White, } impl TryFrom<&str> for Color { type Error = (); fn try_from(value: &str) -> Result { Ok(match value { "^0" => Self::Black, "^1" => Self::Red, "^2" => Self::Green, "^3" => Self::Yellow, "^4" => Self::Blue, "^5" => Self::Cyan, "^6" => Self::Magenta, "^7" => Self::White, _ => return Err(()), }) } } #[inline] pub fn is_color_code(s: &str) -> bool { matches!(s.as_bytes(), [b'^', c, ..] if c.is_ascii_digit()) } #[inline] pub fn trim_start_color(s: &str) -> (&str, &str) { let mut n = 0; while is_color_code(&s[n..]) { n += 2; } if n > 0 { (&s[n - 2..n], &s[n..]) } else { s.split_at(0) } } pub struct ColorIter<'a> { inner: &'a str, } impl<'a> ColorIter<'a> { pub fn new(inner: &'a str) -> Self { Self { inner } } } impl<'a> Iterator for ColorIter<'a> { type Item = (&'a str, &'a str); fn next(&mut self) -> Option { if self.inner.is_empty() { return None; } let (color, tail) = trim_start_color(self.inner); let offset = tail .char_indices() .map(|(i, _)| i) .find(|&i| is_color_code(&tail[i..])) .unwrap_or(tail.len()); let (head, tail) = tail.split_at(offset); self.inner = tail; Some((color, head)) } } pub fn trim_color(s: &str) -> Cow<'_, str> { let (_, s) = trim_start_color(s); if !s.chars().any(|c| c == '^') { return Cow::Borrowed(s); } let mut out = String::with_capacity(s.len()); for (_, s) in ColorIter::new(s) { out.push_str(s); } Cow::Owned(out) } #[cfg(test)] mod tests { use super::*; #[test] fn trim_start_colors() { assert_eq!(trim_start_color("foo^2bar"), ("", "foo^2bar")); assert_eq!(trim_start_color("^foo^2bar"), ("", "^foo^2bar")); assert_eq!(trim_start_color("^1foo^2bar"), ("^1", "foo^2bar")); assert_eq!(trim_start_color("^1^2^3foo^2bar"), ("^3", "foo^2bar")); } #[test] fn trim_colors() { assert_eq!(trim_color("foo^2bar"), "foobar"); assert_eq!(trim_color("^1foo^2bar^3"), "foobar"); assert_eq!(trim_color("^1foo^2bar^3"), "foobar"); assert_eq!(trim_color("^1foo^bar^3"), "foo^bar"); assert_eq!(trim_color("^1foo^2bar^"), "foobar^"); assert_eq!(trim_color("^foo^bar^"), "^foo^bar^"); assert_eq!(trim_color("\u{fe0f}^1foo^bar^"), "\u{fe0f}foo^bar^"); assert_eq!( trim_color("^1^2^3foo\u{fe0f}^2^\u{fe0f}^bar^"), "foo\u{fe0f}^\u{fe0f}^bar^" ); } }