use std::error::Error; use std::fmt; use std::io::Write; use loggers::Logger; use symbols::Symbol; #[derive(Debug,PartialEq)] pub enum SymbolRunError { Symbol(E), 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()) } } } impl From for SymbolRunError { fn from(err: E) -> SymbolRunError { SymbolRunError::Symbol(err) } } pub trait SymbolRunner { fn run_symbol(&self, logger: &mut Logger, symbol: &S) -> Result<(), SymbolRunError> where S::Error: Error; } pub struct InitializingSymbolRunner; impl SymbolRunner for InitializingSymbolRunner { fn run_symbol(&self, logger: &mut Logger, symbol: &S) -> Result<(), SymbolRunError> where S::Error: Error { 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(SymbolRunError::ExecuteDidNotReach(())); } } Ok(()) } } pub struct DrySymbolRunner; impl SymbolRunner for DrySymbolRunner { fn run_symbol(&self, logger: &mut Logger, symbol: &S) -> Result<(), SymbolRunError> where S::Error: Error { 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; use schema::SymbolRunError; #[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") } } #[derive(Debug)] struct DummySymbol { _target_reacheds: Vec>, _cur_target_reached: RefCell, _execute: Result<(), DummySymbolError>, } impl Symbol for DummySymbol { type Error = DummySymbolError; fn target_reached(&self) -> Result { let mut cur = self._cur_target_reached.borrow_mut(); let ret = self._target_reacheds[*cur].clone(); *cur = *cur + 1; ret } fn execute(&self) -> Result<(), Self::Error> { self._execute.clone() } } impl fmt::Display for DummySymbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Dummy symbol") } } impl DummySymbol { fn new(target_reached: Vec>, execute: Result<(), DummySymbolError>) -> DummySymbol { DummySymbol { _target_reacheds: target_reached, _cur_target_reached: RefCell::new(0), _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: Result<(), SymbolRunError> = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(vec![Ok(true)], Ok(())) ); assert_eq!( result, Ok(()) ); } #[test] fn everything_is_ok() { let result: Result<(), SymbolRunError> = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(vec![Ok(false), Ok(true)], Ok(())) ); assert_eq!( result, Ok(()) ); } #[test] fn executing_did_not_change_state() { let result: Result<(), SymbolRunError> = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(vec![Ok(false), Ok(false)], Ok(()))); assert_eq!( result, Err(SymbolRunError::ExecuteDidNotReach(())) ); } #[test] fn executing_did_not_work() { assert_eq!( InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(vec![Ok(false)], Err(DummySymbolError::Error(()))) ), Err(SymbolRunError::Symbol(DummySymbolError::Error(()))) ); } }