console_ui_engine_null 0.1.0

A console UI engine written to learn Rust
Documentation
use std::str::FromStr;

use crossterm::style::{Attribute, Color, ContentStyle};

use crate::buffer::StyledChar;

enum StyleType {
    ForegroundColor(Color),
    BackgroundColor(Color),
    Attribute(Attribute)
}

impl StyleType {
    fn from_str(name: &str) -> StyleType {
        match name.to_lowercase().as_str() {
            "bold" => { StyleType::Attribute(Attribute::Bold) }
            "dim" => { StyleType::Attribute(Attribute::Dim) }
            "italic" => { StyleType::Attribute(Attribute::Italic) }
            "underline" => { StyleType::Attribute(Attribute::Underlined) }
            "slow_blink" => { StyleType::Attribute(Attribute::SlowBlink) }
            "rapid_blink" => { StyleType::Attribute(Attribute::RapidBlink) }
            "reverse" => { StyleType::Attribute(Attribute::Reverse) }
            "hidden" => { StyleType::Attribute(Attribute::Hidden) }
            "crossed_out" => { StyleType::Attribute(Attribute::CrossedOut) }
            "fraktur" => { StyleType::Attribute(Attribute::Fraktur) }
            "framed" => { StyleType::Attribute(Attribute::Framed) }
            "encircled" => { StyleType::Attribute(Attribute::Encircled) }
            "overline" => { StyleType::Attribute(Attribute::OverLined) }
            _ => {
                let mut rgb: Option<Color> = None;
                if name[5..].starts_with("rgb"){
                    let rgb_str = &name[9..name.len()-1];
                    let mut res=rgb_str.split(',').map(|e|e.trim().parse::<u8>()
                        .expect(format!("Failed to parse rich text style: RGB not valid: {}",rgb_str.to_string()).as_str()));
                    let r=res.nth(0).unwrap();
                    let g=res.nth(0).unwrap();
                    let b=res.nth(0).unwrap();
                    rgb = Some(Color::Rgb { r, g, b });
                }

                if name.starts_with("fore:") {
                    if let Some(rgb) = rgb{
                        StyleType::ForegroundColor(rgb)
                    } else {
                        StyleType::ForegroundColor(Color::from_str(&name[5..]).unwrap())
                    }
                } else {
                    if let Some(rgb) = rgb {
                        StyleType::BackgroundColor(rgb)
                    } else {
                        StyleType::BackgroundColor(Color::from_str(&name[5..]).unwrap())
                    }
                }
            }
        }
    }
}

#[derive(Clone)]
pub enum Content {
    Plain(String, Option<ContentStyle>),
    RichText(Vec<StyledChar>)
}

impl Content{
    pub fn from_string(str: String) -> Content {
        Content::Plain(str, None)
    }
    pub fn from_string_styled(str: String, style: Option<ContentStyle>) -> Content {
        Content::Plain(str, style)
    }

    pub fn from_string_parse_style(str: String) -> Content {
        let mut ret = Vec::new();
        let mut current_style = ContentStyle::new();
        let mut style_stack = Vec::new();
        let mut escape = false;
        let mut tag = false;
        let mut current_tag_content = String::new();
        for c in str.chars() {
            if c=='<' {
                if escape { escape = false; }
                else { tag = true; continue; }
            }

            if tag {
                if c=='>' {
                    tag = false;
                    if current_tag_content.starts_with('/'){
                        let style = style_stack.pop().expect("Failed to parse rich text style.");

                        match style {
                            StyleType::ForegroundColor(_) => {
                                current_style.foreground_color = None;
                            },
                            StyleType::BackgroundColor(_) => {
                                current_style.background_color = None;
                            },
                            StyleType::Attribute(attr) => {
                                current_style.attributes.retain(|&e| e!=attr);
                            },
                        }
                    }
                    else {
                        let style = StyleType::from_str(current_tag_content.as_str());
                        match style {
                            StyleType::ForegroundColor(color) => {
                                current_style.foreground_color = Some(color);
                            },
                            StyleType::BackgroundColor(color) => {
                                current_style.background_color = Some(color);
                            },
                            StyleType::Attribute(attr) => {
                                current_style.attributes.push(attr);
                            },
                        }
                        style_stack.push(style);
                    }
                    current_tag_content.clear();
                } else {
                    current_tag_content.push(c);
                }
                continue;
            }

            if c == '\\' {
                escape = true;
                continue;
            }

            ret.push(StyledChar{ style: current_style.clone(), content: c });
        }
        Content::RichText(ret)
    }

    pub fn insert(&mut self, i: usize, ch: char) {
        match self{
            Content::Plain(c, _s) => { c.insert(i, ch); },
            Content::RichText(c) => { c.insert(i, StyledChar::from_char(ch)) },
        }
    }

    pub fn remove(&mut self, i: usize) {
        match self{
            Content::Plain(c, _s) => { c.remove(i); },
            Content::RichText(c) => { c.remove(i); },
        }
    }

    pub fn len(&self) -> usize {
        match self{
            Content::Plain(c, _s) => { c.len() },
            Content::RichText(c) => { c.len() },
        }
    }

    pub fn substr(&self, low: usize, high: usize) -> Content {
        match self{
            Content::Plain(c, s) => { Content::Plain(c[low..high].to_string(), s.clone()) },
            Content::RichText(c) => { Content::RichText(c.as_slice()[low..high].to_vec()) },
        }
    }
}