use std::error::Error; use std::fmt; use loggers::Logger; use symbols::Symbol; #[derive(Debug)] pub enum SymbolRunError { Symbol(Box), ExecuteDidNotReach(()) } impl Error for SymbolRunError { fn description(&self) -> &str { match self { &SymbolRunError::Symbol(_) => "Symbol execution error", &SymbolRunError::ExecuteDidNotReach(_) => "Target not reached after executing symbol" } } fn cause(&self) -> Option<&Error> { match self { &SymbolRunError::Symbol(ref e) => Some(&**e), &SymbolRunError::ExecuteDidNotReach(_) => None } } } impl fmt::Display for SymbolRunError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &SymbolRunError::Symbol(ref e) => write!(f, "{}", e), &SymbolRunError::ExecuteDidNotReach(_) => write!(f, "{}", self.description()) } } } pub trait SymbolRunner { fn run_symbol(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box>; } pub struct InitializingSymbolRunner; impl SymbolRunner for InitializingSymbolRunner { fn run_symbol(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box> { let target_reached = try!(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()); logger.write(format!("Executing {}", symbol).as_str()); try!(symbol.execute()); let target_reached = try!(symbol.target_reached()); logger.debug(format!("Symbol reports target_reached: {:?} (should be true)", target_reached).as_str()); if !target_reached { return Err(Box::new(SymbolRunError::ExecuteDidNotReach(()))); } } Ok(()) } } pub struct DrySymbolRunner; impl SymbolRunner for DrySymbolRunner { fn run_symbol(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box> { let target_reached = try!(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()); } Ok(()) } } // FIXME: Add ExpectingSymbolRunner #[cfg(test)] mod test { use std::cell::RefCell; use std::error::Error; use std::fmt; use loggers::Logger; use symbols::Symbol; use schema::SymbolRunner; use schema::InitializingSymbolRunner; #[derive(Debug, PartialEq, Clone)] enum DummySymbolError { Error(()) } impl Error for DummySymbolError { fn description(&self) -> &str { return "Description"; } } impl fmt::Display for DummySymbolError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Dummy symbol error") } } struct DummySymbol<'a> { _execute: &'a Fn() -> Result<(), Box>, _target_reached: &'a Fn() -> Result> } impl<'a> Symbol for DummySymbol<'a> { fn target_reached(&self) -> Result> { (self._target_reached)() } fn execute(&self) -> Result<(), Box> { (self._execute)() } } impl<'a> fmt::Display for DummySymbol<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Dummy symbol") } } impl<'a> DummySymbol<'a> { fn new(target_reached: &'a Fn() -> Result>, execute: &'a Fn() -> Result<(), Box>) -> DummySymbol<'a> { DummySymbol { _target_reached: target_reached, _execute: execute } } } 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(From::from(line)); } fn debug(&mut self, line: &str) { self.log.push(From::from(line)); } } #[test] fn nothing_needed_to_be_done() { let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(true), &|| Ok(())) ); assert!(result.is_ok()); } #[test] fn everything_is_ok() { let first = RefCell::new(true); let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| { let mut _first = first.borrow_mut(); Ok(if *_first { *_first = false; true } else { false }) }, &|| Ok(())) ); assert!(result.is_ok()); } #[test] fn executing_did_not_change_state() { let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(false), &|| Ok(()))); assert_eq!( result.unwrap_err().description(), "Target not reached after executing symbol" ); } #[test] fn executing_did_not_work() { let err = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(false), &|| Err(Box::new(DummySymbolError::Error(()))) )).unwrap_err(); assert_eq!(err.description(), "Description"); } }