A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
5.5 KiB

use std::error::Error;
use std::fmt;
use loggers::Logger;
use symbols::Symbol;
#[derive(Debug,PartialEq)]
pub enum SymbolRunError<E: Error> {
Symbol(E),
ExecuteDidNotReach(())
}
impl<E: Error> Error for SymbolRunError<E> {
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<E: Error> fmt::Display for SymbolRunError<E> {
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<E: Error> From<E> for SymbolRunError<E> {
fn from(err: E) -> SymbolRunError<E> {
SymbolRunError::Symbol(err)
}
}
pub trait SymbolRunner {
fn run_symbol<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), SymbolRunError<S::Error>>
where S::Error: Error;
}
pub struct InitializingSymbolRunner;
impl SymbolRunner for InitializingSymbolRunner {
fn run_symbol<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), SymbolRunError<S::Error>>
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<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), SymbolRunError<S::Error>>
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<Result<bool, DummySymbolError>>,
_cur_target_reached: RefCell<usize>,
_execute: Result<(), DummySymbolError>,
}
impl Symbol for DummySymbol {
type Error = DummySymbolError;
fn target_reached(&self) -> Result<bool, Self::Error> {
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<Result<bool, DummySymbolError>>,
execute: Result<(), DummySymbolError>) -> DummySymbol {
DummySymbol { _target_reacheds: target_reached, _cur_target_reached: RefCell::new(0), _execute: execute }
}
}
struct DummyLogger {
log: Vec<String>
}
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<DummySymbolError>> = 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<DummySymbolError>> = 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<DummySymbolError>> = 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(())))
);
}
}