voidio 0.1.5

VOID I/O - High-performance Cross-platform I/O for Rust.
use std::{fmt, ops};
use std::rc::Rc;
use crate::console::{Component, SendableComponent};

fn rgba_to_ansi(rgba: u32) -> String {
    let r = (rgba >> 16) & 0xFF;
    let g = (rgba >> 8) & 0xFF;
    let b = rgba & 0xFF;
    // 24-bit true color ANSI format
    format!("38;2;{};{};{}", r, g, b)
}

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

#[derive(Clone)]
pub struct TextComponent(pub Rc<TextComponentData>);

impl TextComponent {
    pub fn new(text: &str) -> TextComponent {
        TextComponent(Rc::new(TextComponentData {
            color: None,
            text: text.to_string(),
            parts: Vec::new(),
        }))
    }
    pub fn with_color(self, color: u32) -> TextComponent {
        TextComponent(Rc::new(TextComponentData {
            color: Some(color),
            text: self.0.text.clone(),
            parts: self.0.parts.clone(),
        }))
    }
}

impl fmt::Display for TextComponent {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(color) = &self.0.color {
            let ansi_color = rgba_to_ansi(*color);
            write!(f, "\x1b[{}m", ansi_color)?;
            write!(f, "{}", self.0.text)?;
            write!(f, "\x1b[0m")?;
        } else {
            write!(f, "{}", self.0.text)?;
        }
        for part in &self.0.parts {
            write!(f, "{}", part)?;
        }
        Ok(())
    }
}

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

impl Component {
    pub fn empty() -> TextComponent {
        TextComponent::new("")
    }
    pub fn text(text: &str) -> TextComponent {
        TextComponent::new(text)
    }
}

impl ops::Add for TextComponent {
    type Output = Self;
    fn add(self, other: Self) -> Self::Output {
        let mut new_parts = self.0.parts.clone();
        new_parts.push(Box::new(other.clone()));

        TextComponent(Rc::new(TextComponentData {
            color: self.0.color,
            text: self.0.text.clone(),
            parts: new_parts,
        }))
    }
}