#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Attribute {
Bold,
Italic,
Underline,
Colour(u8),
}
#[must_use]
pub fn decoder(input: &str) -> Vec<(String, Vec<Attribute>)> {
let mut output = Vec::new();
let mut current_text = String::new();
let mut current_attributes = Vec::new();
let mut attribute_stack = Vec::new();
let mut chars = input.chars().peekable();
while let Some(c) = chars.next() {
match c {
'\x02' => {
if current_attributes.contains(&Attribute::Bold) {
current_attributes.retain(|&a| a != Attribute::Bold);
attribute_stack.pop();
} else {
current_attributes.push(Attribute::Bold);
attribute_stack.push(Attribute::Bold);
}
}
'\x1D' => {
if current_attributes.contains(&Attribute::Italic) {
current_attributes.retain(|&a| a != Attribute::Italic);
attribute_stack.pop();
} else {
current_attributes.push(Attribute::Italic);
attribute_stack.push(Attribute::Italic);
}
}
'\x1F' => {
if current_attributes.contains(&Attribute::Underline) {
current_attributes.retain(|&a| a != Attribute::Underline);
attribute_stack.pop();
} else {
current_attributes.push(Attribute::Underline);
attribute_stack.push(Attribute::Underline);
}
}
'\x03' => {
let mut color_code = String::new();
if let Some(next_char) = chars.peek() {
if next_char.is_ascii_digit() {
color_code.push(*next_char);
chars.next();
}
}
if let Some(next_char) = chars.peek() {
if next_char.is_ascii_digit() {
color_code.push(*next_char);
chars.next();
}
}
if !color_code.is_empty() {
let color_num = color_code.parse::<u8>().unwrap_or(0);
current_attributes.push(Attribute::Colour(color_num));
attribute_stack.push(Attribute::Colour(color_num));
}
}
'\x0F' => {
current_attributes.clear();
attribute_stack.clear();
}
_ => current_text.push(c),
}
if !attribute_stack.is_empty() {
current_attributes = attribute_stack.clone();
}
if !current_text.is_empty() {
output.push((current_text.clone(), current_attributes.clone()));
current_text.clear();
}
}
if !current_text.is_empty() {
output.push((current_text, current_attributes));
}
output
}