mprocs 0.7.2

TUI for running multiple processes
use std::fmt::Debug;

use crossterm::event::Event;
use serde::{Deserialize, Serialize};
use tui::{backend::Backend, style::Modifier};

use crate::{error::ResultLogger, host::sender::MsgSender};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum SrvToClt {
  Draw { cells: Vec<(u16, u16, Cell)> },
  SetCursor { x: u16, y: u16 },
  ShowCursor,
  HideCursor,
  CursorShape(CursorStyle),
  Clear,
  Flush,
  Quit,
}

#[derive(
  Debug, Default, Deserialize, Clone, Copy, PartialEq, Eq, Serialize,
)]
pub enum CursorStyle {
  #[default]
  Default = 0,
  BlinkingBlock = 1,
  SteadyBlock = 2,
  BlinkingUnderline = 3,
  SteadyUnderline = 4,
  BlinkingBar = 5,
  SteadyBar = 6,
}

impl From<termwiz::escape::csi::CursorStyle> for CursorStyle {
  fn from(value: termwiz::escape::csi::CursorStyle) -> Self {
    use termwiz::escape::csi::CursorStyle as CS;

    match value {
      CS::Default => Self::Default,
      CS::BlinkingBlock => Self::BlinkingBlock,
      CS::SteadyBlock => Self::SteadyBlock,
      CS::BlinkingUnderline => Self::BlinkingUnderline,
      CS::SteadyUnderline => Self::SteadyUnderline,
      CS::BlinkingBar => Self::BlinkingBar,
      CS::SteadyBar => Self::SteadyBar,
    }
  }
}

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum CltToSrv {
  Init { width: u16, height: u16 },
  Key(Event),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Cell {
  str: String,
  fg: Color,
  bg: Color,
  underline_color: Color,
  mods: Modifier,
  skip: bool,
}

impl From<&Cell> for tui::buffer::Cell {
  fn from(value: &Cell) -> Self {
    let mut cell = tui::buffer::Cell::default();
    cell.set_symbol(&value.str);
    cell.set_style(
      tui::style::Style::new()
        .fg(value.fg.into())
        .bg(value.bg.into())
        .underline_color(value.underline_color.into())
        .add_modifier(value.mods),
    );
    cell.set_skip(value.skip);
    cell
  }
}

impl From<&tui::buffer::Cell> for Cell {
  fn from(value: &tui::buffer::Cell) -> Self {
    Cell {
      str: value.symbol().to_string(),
      fg: value.fg.into(),
      bg: value.bg.into(),
      underline_color: value.underline_color.into(),
      mods: value.modifier,
      skip: value.skip,
    }
  }
}

#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum Color {
  Reset,
  Black,
  Red,
  Green,
  Yellow,
  Blue,
  Magenta,
  Cyan,
  Gray,
  DarkGray,
  LightRed,
  LightGreen,
  LightYellow,
  LightBlue,
  LightMagenta,
  LightCyan,
  White,
  Rgb(u8, u8, u8),
  Indexed(u8),
}

impl From<Color> for tui::style::Color {
  fn from(value: Color) -> Self {
    match value {
      Color::Reset => tui::style::Color::Reset,
      Color::Black => tui::style::Color::Black,
      Color::Red => tui::style::Color::Red,
      Color::Green => tui::style::Color::Green,
      Color::Yellow => tui::style::Color::Yellow,
      Color::Blue => tui::style::Color::Blue,
      Color::Magenta => tui::style::Color::Magenta,
      Color::Cyan => tui::style::Color::Cyan,
      Color::Gray => tui::style::Color::Gray,
      Color::DarkGray => tui::style::Color::DarkGray,
      Color::LightRed => tui::style::Color::LightRed,
      Color::LightGreen => tui::style::Color::LightGreen,
      Color::LightYellow => tui::style::Color::LightYellow,
      Color::LightBlue => tui::style::Color::LightBlue,
      Color::LightMagenta => tui::style::Color::LightMagenta,
      Color::LightCyan => tui::style::Color::LightCyan,
      Color::White => tui::style::Color::White,
      Color::Rgb(r, g, b) => tui::style::Color::Rgb(r, g, b),
      Color::Indexed(idx) => tui::style::Color::Indexed(idx),
    }
  }
}

impl From<tui::style::Color> for Color {
  fn from(value: tui::style::Color) -> Self {
    match value {
      tui::style::Color::Reset => Color::Reset,
      tui::style::Color::Black => Color::Black,
      tui::style::Color::Red => Color::Red,
      tui::style::Color::Green => Color::Green,
      tui::style::Color::Yellow => Color::Yellow,
      tui::style::Color::Blue => Color::Blue,
      tui::style::Color::Magenta => Color::Magenta,
      tui::style::Color::Cyan => Color::Cyan,
      tui::style::Color::Gray => Color::Gray,
      tui::style::Color::DarkGray => Color::DarkGray,
      tui::style::Color::LightRed => Color::LightRed,
      tui::style::Color::LightGreen => Color::LightGreen,
      tui::style::Color::LightYellow => Color::LightYellow,
      tui::style::Color::LightBlue => Color::LightBlue,
      tui::style::Color::LightMagenta => Color::LightMagenta,
      tui::style::Color::LightCyan => Color::LightCyan,
      tui::style::Color::White => Color::White,
      tui::style::Color::Rgb(r, g, b) => Color::Rgb(r, g, b),
      tui::style::Color::Indexed(idx) => Color::Indexed(idx),
    }
  }
}

pub struct ProxyBackend {
  pub tx: MsgSender<SrvToClt>,
  pub height: u16,
  pub width: u16,
  pub x: u16,
  pub y: u16,
}

impl ProxyBackend {
  fn send(&mut self, msg: SrvToClt) {
    self.tx.send(msg).log_ignore()
  }

  pub fn set_size(&mut self, width: u16, height: u16) {
    self.width = width;
    self.height = height;
  }
}

impl Backend for ProxyBackend {
  fn draw<'a, I>(&mut self, content: I) -> Result<(), std::io::Error>
  where
    I: Iterator<Item = (u16, u16, &'a tui::buffer::Cell)>,
  {
    let msg = SrvToClt::Draw {
      cells: content
        .map(|(a, b, cell)| (a, b, Cell::from(cell)))
        .collect(),
    };
    self.send(msg);
    Ok(())
  }

  fn hide_cursor(&mut self) -> Result<(), std::io::Error> {
    self.send(SrvToClt::HideCursor);
    Ok(())
  }

  fn show_cursor(&mut self) -> Result<(), std::io::Error> {
    self.send(SrvToClt::ShowCursor);
    Ok(())
  }

  fn get_cursor(&mut self) -> Result<(u16, u16), std::io::Error> {
    Ok((self.x, self.y))
  }

  fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), std::io::Error> {
    self.x = x;
    self.y = y;
    self.send(SrvToClt::SetCursor { x, y });
    Ok(())
  }

  fn clear(&mut self) -> Result<(), std::io::Error> {
    self.send(SrvToClt::Clear);
    Ok(())
  }

  fn window_size(&mut self) -> std::io::Result<tui::backend::WindowSize> {
    let win_size = tui::backend::WindowSize {
      columns_rows: tui::layout::Size {
        width: self.width,
        height: self.height,
      },
      pixels: tui::layout::Size {
        width: 0,
        height: 0,
      },
    };
    Ok(win_size)
  }

  fn size(&self) -> Result<tui::layout::Rect, std::io::Error> {
    let rect = tui::layout::Rect::new(0, 0, self.width, self.height);
    Ok(rect)
  }

  fn flush(&mut self) -> Result<(), std::io::Error> {
    self.send(SrvToClt::Flush);
    Ok(())
  }
}