use crate::loggers::Logger; use crate::symbols::Symbol; use std::cell::RefCell; use std::error::Error; use std::fmt; use std::fmt::Debug; pub trait SymbolRunner { fn run_symbol(&self, symbol: &S, force: bool) -> Result>; } impl SymbolRunner for Box { fn run_symbol(&self, symbol: &S, force: bool) -> Result> { (**self).run_symbol(symbol, force) } } #[derive(Debug)] pub enum SymbolRunError { Symbol(Box), ExecuteDidNotReach(()), } impl Error for SymbolRunError { fn cause(&self) -> Option<&dyn Error> { match self { Self::Symbol(ref e) => Some(&**e), Self::ExecuteDidNotReach(_) => None, } } } impl fmt::Display for SymbolRunError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Symbol(ref e) => write!(f, "{}", e), Self::ExecuteDidNotReach(_) => write!(f, "Target not reached after executing symbol"), } } } pub struct InitializingSymbolRunner { logger: RefCell, } impl InitializingSymbolRunner { pub fn new(logger: L) -> Self { Self { logger: RefCell::new(logger), } } fn exec_symbol(&self, symbol: &S) -> Result<(), Box> { let mut logger = self.logger.borrow_mut(); logger.write(format!("Executing {:?}", symbol).as_str()); symbol.execute()?; let target_reached = symbol.target_reached()?; logger.debug( format!( "Symbol reports target_reached: {:?} (should be true)", target_reached ) .as_str(), ); if target_reached { Ok(()) } else { Err(Box::new(SymbolRunError::ExecuteDidNotReach(()))) } } } impl SymbolRunner for InitializingSymbolRunner { fn run_symbol(&self, symbol: &S, force: bool) -> Result> { let mut logger = self.logger.borrow_mut(); let executed = if force { logger.debug("Forcing symbol execution"); drop(logger); self.exec_symbol(symbol)?; true } else { let target_reached = symbol.target_reached()?; if target_reached { logger.write(format!("{:?} already reached", symbol).as_str()); } else { logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str()); drop(logger); self.exec_symbol(symbol)?; } !target_reached }; Ok(executed) } } pub struct DrySymbolRunner { logger: RefCell, } impl DrySymbolRunner { pub fn new(logger: L) -> Self { Self { logger: RefCell::new(logger), } } } impl SymbolRunner for DrySymbolRunner { fn run_symbol(&self, symbol: &S, force: bool) -> Result> { let mut logger = self.logger.borrow_mut(); let would_execute = if force { logger.write(format!("Would force-execute {:?}", symbol).as_str()); true } else { let target_reached = symbol.target_reached()?; logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str()); if !target_reached { logger.write(format!("Would execute {:?}", symbol).as_str()); } !target_reached }; Ok(would_execute) } } pub struct ReportingSymbolRunner<'a, R, L>(&'a R, RefCell); impl<'a, R, L> ReportingSymbolRunner<'a, R, L> { pub fn new(symbol_runner: &'a R, logger: L) -> Self { ReportingSymbolRunner(symbol_runner, RefCell::new(logger)) } } impl<'a, R, L> SymbolRunner for ReportingSymbolRunner<'a, R, L> where R: SymbolRunner, L: Logger, { fn run_symbol(&self, symbol: &S, force: bool) -> Result> { let mut logger = self.1.borrow_mut(); logger.debug(format!("Running symbol {:?}", symbol).as_str()); let res = self.0.run_symbol(symbol, force); if let Err(ref e) = res { logger.write(format!("Failed on {:?} with {}, aborting.", symbol, e).as_str()) } else { logger.debug(format!("Successfully finished {:?}", symbol).as_str()) } res } } #[cfg(test)] mod test { use std::cell::RefCell; use std::error::Error; use std::fmt; use crate::loggers::Logger; use crate::schema::InitializingSymbolRunner; use crate::schema::SymbolRunner; use crate::symbols::Symbol; #[derive(Debug, PartialEq, Clone)] enum DummySymbolError { Error(()), } impl Error for DummySymbolError {} impl fmt::Display for DummySymbolError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Dummy symbol error") } } #[derive(Debug)] struct DummySymbol { _target_reached: RefCell, _execute: RefCell, } impl< E: Iterator>>, T: Iterator>>, > Symbol for DummySymbol { fn target_reached(&self) -> Result> { self._target_reached.borrow_mut().next().unwrap() } fn execute(&self) -> Result<(), Box> { self._execute.borrow_mut().next().unwrap() } } impl< E: Iterator>>, T: Iterator>>, > DummySymbol { fn new< IE: IntoIterator>>, IT: IntoIterator>>, >( target_reached: IT, execute: IE, ) -> Self { Self { _target_reached: RefCell::new(target_reached.into_iter()), _execute: RefCell::new(execute.into_iter()), } } } struct DummyLogger { log: Vec, } impl DummyLogger { fn new() -> DummyLogger { DummyLogger { log: Vec::new() } } } impl Logger for DummyLogger { fn write(&mut self, line: &str) { self.log.push(line.into()); } fn debug(&mut self, line: &str) { self.log.push(line.into()); } } #[test] fn nothing_needed_to_be_done() { let result = InitializingSymbolRunner::new(DummyLogger::new()) .run_symbol(&DummySymbol::new(vec![Ok(true)], vec![Ok(())]), false); assert!(result.is_ok()); } #[test] fn everything_is_ok() { let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol( &DummySymbol::new(vec![Ok(true), Ok(false)], vec![Ok(())]), false, ); assert!(result.is_ok()); } #[test] fn executing_did_not_change_state() { let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol( &DummySymbol::new(vec![Ok(false), Ok(false)], vec![Ok(())]), false, ); assert_eq!( result.unwrap_err().to_string(), "Target not reached after executing symbol" ); } #[test] fn executing_did_not_work() { let err = InitializingSymbolRunner::new(DummyLogger::new()) .run_symbol( &DummySymbol::new( vec![Ok(false)], vec![Err(Box::new(DummySymbolError::Error(())) as Box)], ), false, ) .unwrap_err(); assert_eq!(err.to_string(), "Dummy symbol error"); } }