voidio 0.1.4

VOID I/O - High-performance Cross-platform I/O for Rust.
use std::{fmt, ops};
pub trait SendableComponent: fmt::Display {
    fn as_any(&self) -> &dyn std::any::Any;
    fn clone_box(&self) -> Box<dyn SendableComponent>;
}
impl Clone for Box<dyn SendableComponent> {
    fn clone(&self) -> Self {
        self.clone_box()
    }
}

#[derive(Clone)]
pub struct TextComponent {
    color: Option<u32>,
    text: String,
    parts: Vec<Box<dyn SendableComponent>>,
}

impl TextComponent {
    pub fn color(mut self, color: u32) -> Self {
        self.color = Some(color);
        self
    }
}

fn rgba_to_ansi(rgba: u32) -> String {
    let r = (rgba >> 16) & 0xFF;
    let g = (rgba >> 8) & 0xFF;
    let b = rgba & 0xFF;

    // Use 24-bit true color ANSI format
    format!("38;2;{};{};{}", r, g, b)
}

impl fmt::Display for TextComponent {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Define a closure to handle writing parts
        let write_parts = |f: &mut fmt::Formatter<'_>| -> fmt::Result {
            if self.parts.is_empty() {
                Ok(())
            } else {
                for part in &self.parts {
                    write!(f, "{}", part)?;
                }
                Ok(())
            }
        };

        if let Some(color) = &self.color {
            // If color is specified, write the ANSI escape code
            let ansi_color = rgba_to_ansi(*color);
            write!(f, "\x1b[{}m", ansi_color)?;
            write!(f, "{}", self.text)?;
            // Reset color after writing the text
            write!(f, "\x1b[0m")?;
        } else {
            write!(f, "{}", self.text)?;
        }

        // Call the helper function after writing the text
        write_parts(f)
    }
}

impl SendableComponent for TextComponent {
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }

    fn clone_box(&self) -> Box<dyn SendableComponent> {
        Box::new(self.clone())
    }
}

#[allow(dead_code)]
pub struct Component {
    parts: Vec<Box<dyn SendableComponent>>,
}
impl Component {
    pub fn empty() -> TextComponent {
        TextComponent {
            color: None,
            text: String::new(),
            parts: Vec::new(),
        }
    }
    pub fn text(text: &str) -> TextComponent {
        TextComponent {
            color: None,
            text: text.to_string(),
            parts: Vec::new(),
        }
    }
}

impl ops::Add<TextComponent> for TextComponent {
    type Output = TextComponent;
    fn add(mut self, other: TextComponent) -> Self::Output {
        self.parts.push(Box::new(TextComponent {
            color: other.color,
            text: other.text,
            parts: Vec::new(),
        }));
        for comp in other.parts {
            self.parts.push(comp);
        }
        self
    }
}