tutti-cli 0.1.5

Command-line interface for Tutti
use colored::{Color, Colorize};
use std::hash::{DefaultHasher, Hash, Hasher};
use std::io::{self, Stdout, Write};

pub struct Logger<W: Write = Stdout> {
    output: W,
}

impl<W: Write> Logger<W> {
    pub fn new(output: W) -> Self {
        Self { output }
    }

    fn string_to_color(s: &str) -> Color {
        let colors = [
            Color::Green,
            Color::Blue,
            Color::Magenta,
            Color::Cyan,
            Color::BrightGreen,
            Color::BrightBlue,
            Color::BrightMagenta,
            Color::BrightCyan,
        ];

        let mut hasher = DefaultHasher::new();
        s.hash(&mut hasher);
        let hash = hasher.finish();

        let idx = usize::try_from(hash).unwrap_or_default() % colors.len();
        colors[idx]
    }

    pub fn log(&mut self, service_name: &str, message: &str) {
        let prefix = format!("[{service_name}]").color(Self::string_to_color(service_name));
        for line in message.lines() {
            let _ = writeln!(self.output, "{prefix} {line}");
        }
    }

    pub fn system(&mut self, message: &str) {
        let prefix = "[system]".color(Color::Red);
        for line in message.lines() {
            let _ = writeln!(self.output, "{prefix} {line}");
        }
    }
}

impl Logger {
    pub fn default() -> Self {
        Self::new(io::stdout())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;

    #[test]
    fn test_log() {
        let buffer = Vec::new();
        let mut logger = Logger::new(Cursor::new(buffer));

        logger.log("test", "line1\nline2");

        let output = String::from_utf8(logger.output.into_inner()).unwrap();
        let service = "[test]".color(Color::BrightGreen);
        let line1 = format!("{service} line1");
        let line2 = format!("{service} line2");
        assert_eq!(output, format!("{line1}\n{line2}\n"));
    }

    #[test]
    fn test_log_default() {
        let _logger = Logger::default();
    }
}