epkard 0.1.1

A generalized framework for creating branching narratives
Documentation
use std::{
    io::{stdin, stdout, BufRead, Write},
    ops::Neg,
};

use colored::Colorize;

use crate::{Node, Runtime};

/// Colors used for frontends
#[allow(missing_docs)]
pub enum Color {
    White,
    Black,
    Red,
    Green,
    Blue,
    Cyan,
    Magenta,
    Yellow,
}

impl Neg for Color {
    type Output = Self;
    fn neg(self) -> Self::Output {
        use Color::*;
        match self {
            White => Black,
            Black => White,
            Red => Cyan,
            Cyan => Red,
            Green => Magenta,
            Magenta => Green,
            Blue => Yellow,
            Yellow => Blue,
        }
    }
}

/**

The frontend for a narrative that a user can interact with
*/
pub trait Frontend {
    /// Get the background color
    fn background_color(&self) -> Color;
    /// Set the background color
    fn set_background_color(&mut self, color: Color);
    /// Clear the display
    fn clear(&mut self);
    /// Print text to the display with the given color
    fn print_colored<S: ToString>(&mut self, text: S, color: Color);
    /// Print a newline to the display
    fn newline(&mut self);
    /// Get input from the user
    fn input(&mut self) -> String;
    /// Print text to the display
    fn print<S: ToString>(&mut self, text: S) {
        self.print_colored(text, -self.background_color());
    }
    /// Print text and a newline to the display
    fn println<S: ToString>(&mut self, text: S) {
        self.print(text);
        self.newline();
    }
}

/// A frontend that uses stdin and stdout
pub struct CliFrontend {
    background_color: colored::Color,
}

impl CliFrontend {
    /// Create a new `CliFrontend`
    pub fn new() -> Self {
        Self::default()
    }
    /// Create a new `CliFrontend` with the given background color
    pub fn with_bg_color(color: Color) -> Self {
        let mut clif = Self::new();
        clif.set_background_color(color);
        clif
    }
}

impl Default for CliFrontend {
    fn default() -> Self {
        CliFrontend {
            background_color: colored::Color::Black,
        }
    }
}

impl Frontend for CliFrontend {
    fn background_color(&self) -> Color {
        use Color::*;
        match self.background_color {
            colored::Color::Black => Black,
            colored::Color::White => White,
            colored::Color::Red => Red,
            colored::Color::Green => Green,
            colored::Color::Blue => Blue,
            colored::Color::Cyan => Cyan,
            colored::Color::Magenta => Magenta,
            colored::Color::Yellow => Yellow,
            _ => Black,
        }
    }
    fn set_background_color(&mut self, color: Color) {
        use Color::*;
        self.background_color = match color {
            White => colored::Color::Black,
            Black => colored::Color::White,
            Red => colored::Color::Red,
            Green => colored::Color::Green,
            Blue => colored::Color::Blue,
            Cyan => colored::Color::Cyan,
            Magenta => colored::Color::Magenta,
            Yellow => colored::Color::Yellow,
        };
    }
    fn clear(&mut self) {
        let (_, height) = terminal_size::terminal_size()
            .unwrap_or_else(|| panic!("{}", "Unable to get terminal size".bright_red()));
        println!("{}", (0..height.0).map(|_| '\n').collect::<String>());
    }
    fn print_colored<S: ToString>(&mut self, text: S, color: Color) {
        use Color::*;
        let color = match color {
            Black => Colorize::black,
            White => Colorize::white,
            Red => Colorize::red,
            Green => Colorize::green,
            Blue => Colorize::blue,
            Cyan => Colorize::cyan,
            Magenta => Colorize::magenta,
            Yellow => Colorize::yellow,
        };
        print!(
            "{}",
            color(text.to_string().as_ref()).on_color(self.background_color)
        );
        let _ = stdout().flush();
    }
    fn newline(&mut self) {
        println!();
    }
    fn input(&mut self) -> String {
        stdin()
            .lock()
            .lines()
            .next()
            .and_then(Result::ok)
            .unwrap_or_else(|| panic!("{}", "Failed to read line".bright_red()))
    }
}

impl<'a, N, F> Frontend for Runtime<'a, N, F>
where
    N: Node,
    F: Frontend,
{
    fn background_color(&self) -> Color {
        self.frontend.background_color()
    }
    fn set_background_color(&mut self, color: Color) {
        self.frontend.set_background_color(color)
    }
    fn clear(&mut self) {
        self.frontend.clear()
    }
    fn print_colored<S: ToString>(&mut self, text: S, color: Color) {
        self.frontend.print_colored(text, color)
    }
    fn newline(&mut self) {
        self.frontend.newline()
    }
    fn input(&mut self) -> String {
        self.frontend.input()
    }
}