tokio-bist 0.2.0

Bult-in self-test runner for Tokio-based applications
Documentation
use std::collections::BTreeMap;

use superconsole::{Component, Dimensions, DrawMode, Line, Lines, SuperConsole};
use superconsole::{Span, components::DrawVertical, style::Color};

pub struct Scons {
    console: Option<SuperConsole>,
    names: BTreeMap<usize, String>,
}

impl Scons {
    pub fn new() -> Self {
        Self {
            console: SuperConsole::new(),
            names: BTreeMap::new(),
        }
    }

    pub fn insert(&mut self, id: usize, name: String) {
        self.names.insert(id, name);

        if let Some(console) = &mut self.console {
            console.render(&Root { names: &self.names }).unwrap();
        }
    }

    pub fn remove(&mut self, id: usize, res: &eyre::Result<crate::Success>) -> String {
        let name = self.names.remove(&id).expect("id must be present");

        let line = res_line(&name, res);

        if let Some(console) = &mut self.console {
            if let Some(line) = line {
                console.emit(Lines(vec![line]));
            }

            console.render(&Root { names: &self.names }).unwrap();
        } else if let Some(line) = line {
            println!("{}", line.render());
        }

        name
    }

    pub fn finalize(self) {
        if let Some(console) = self.console {
            console.finalize(&Root { names: &self.names }).unwrap();
        }
    }
}

fn res_line(name: &str, res: &eyre::Result<crate::Success>) -> Option<Line> {
    let (color, text) = match res {
        Ok(success) => {
            if let Some(warn) = success.warning() {
                (Color::Yellow, format!("{name} WARN: {warn}"))
            } else if success.branches().is_empty() {
                (Color::Green, format!("{name} OK"))
            } else {
                return None;
            }
        }
        Err(err) => (Color::Red, format!("{name} ERROR: {err:#}")),
    };

    Some(Line::from_iter([Span::new_colored_lossy(&text, color)]))
}

struct Root<'a> {
    names: &'a BTreeMap<usize, String>,
}

impl Component for Root<'_> {
    fn draw_unchecked(&self, dimensions: Dimensions, mode: DrawMode) -> anyhow::Result<Lines> {
        let mut vert = DrawVertical::new(dimensions);
        vert.draw(&InProgressList { names: self.names }, mode)?;
        Ok(vert.finish())
    }
}

struct InProgressList<'a> {
    names: &'a BTreeMap<usize, String>,
}

impl Component for InProgressList<'_> {
    fn draw_unchecked(&self, _dimensions: Dimensions, _mode: DrawMode) -> anyhow::Result<Lines> {
        let mut lines = Lines::new();

        for name in self.names.values().take(10) {
            lines.push(Line::sanitized(name));
        }

        Ok(lines)
    }
}