use std::fmt::Display;
use crate::command::{ApplyCommandError, ApplyOutcome, Commander, read_command};
use crate::terminal::{AccessTerminalError, Terminal};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RunFlag {
Running,
Stopped
}
impl Default for RunFlag {
fn default() -> Self {
Self::Stopped
}
}
impl RunFlag {
pub fn start(&mut self) {
*self = Self::Running;
}
pub fn stop(&mut self) {
*self = Self::Stopped;
}
pub fn is_running(&self) -> bool {
matches!(self, Self::Running)
}
}
pub struct Looper<'a, C, E, T: Terminal> {
terminal: &'a mut T,
commander: &'a Commander<C, E, T>,
run_flag: RunFlag,
context: &'a mut C
}
impl<'a, C, E, T: Terminal> Looper<'a, C, E, T> {
pub fn new(terminal: &'a mut T, commander: &'a Commander<C, E, T>, context: &'a mut C) -> Self {
Self {
terminal,
commander,
run_flag: RunFlag::default(),
context
}
}
pub fn terminal(&mut self) -> &mut T {
self.terminal
}
pub fn commander(&self) -> &Commander<C, E, T> {
self.commander
}
pub fn run_flag(&mut self) -> &mut RunFlag {
&mut self.run_flag
}
pub fn split(&mut self) -> (&mut T, &Commander<C, E, T>, &mut C) {
(self.terminal, self.commander, self.context)
}
pub fn context(&mut self) -> &mut C {
self.context
}
}
enum LastCommandOutcome {
Applied,
Skipped,
Erred
}
impl LastCommandOutcome {
fn prompt(&self) -> &'static str {
match self {
LastCommandOutcome::Applied => "+>> ",
LastCommandOutcome::Skipped => "->> ",
LastCommandOutcome::Erred => "!>> "
}
}
}
impl From<ApplyOutcome> for LastCommandOutcome {
fn from(outcome: ApplyOutcome) -> Self {
match outcome {
ApplyOutcome::Applied => Self::Applied,
ApplyOutcome::Skipped => Self::Skipped
}
}
}
impl<'a, C, E: Display, T: Terminal> Looper<'a, C, E, T> {
pub fn run(&mut self) -> Result<(), AccessTerminalError> {
self.run_flag.start();
let mut last_command_outcome = LastCommandOutcome::Applied;
while self.run_flag.is_running() {
let mut command = read_command(self, last_command_outcome.prompt())?;
let result = command.apply(self);
match result {
Ok(apply_outcome) => {
last_command_outcome = apply_outcome.into();
},
Err(ApplyCommandError::Application(err)) => {
self.terminal.print_line(&format!("Command error: {err}."))?;
last_command_outcome = LastCommandOutcome::Erred;
},
Err(ApplyCommandError::AccessTerminal(err)) => {
return Err(err)
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests;