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.
274 lines
7.1 KiB
274 lines
7.1 KiB
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<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>>;
|
|
}
|
|
|
|
impl<R: SymbolRunner + ?Sized> SymbolRunner for Box<R> {
|
|
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
(**self).run_symbol(symbol, force)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum SymbolRunError {
|
|
Symbol(Box<dyn Error>),
|
|
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<L: Logger> {
|
|
logger: RefCell<L>,
|
|
}
|
|
|
|
impl<L: Logger> InitializingSymbolRunner<L> {
|
|
pub fn new(logger: L) -> Self {
|
|
Self {
|
|
logger: RefCell::new(logger),
|
|
}
|
|
}
|
|
|
|
fn exec_symbol<S: Symbol + Debug>(&self, symbol: &S) -> Result<(), Box<dyn Error>> {
|
|
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<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
|
|
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
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<L: Logger> {
|
|
logger: RefCell<L>,
|
|
}
|
|
|
|
impl<L: Logger> DrySymbolRunner<L> {
|
|
pub fn new(logger: L) -> Self {
|
|
Self {
|
|
logger: RefCell::new(logger),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<L: Logger> SymbolRunner for DrySymbolRunner<L> {
|
|
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
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<L>);
|
|
|
|
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<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
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<T, E> {
|
|
_target_reached: RefCell<T>,
|
|
_execute: RefCell<E>,
|
|
}
|
|
|
|
impl<
|
|
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
|
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
|
> Symbol for DummySymbol<T, E>
|
|
{
|
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
|
self._target_reached.borrow_mut().next().unwrap()
|
|
}
|
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
|
self._execute.borrow_mut().next().unwrap()
|
|
}
|
|
}
|
|
|
|
impl<
|
|
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
|
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
|
> DummySymbol<T, E>
|
|
{
|
|
fn new<
|
|
IE: IntoIterator<IntoIter = E, Item = Result<(), Box<dyn Error>>>,
|
|
IT: IntoIterator<IntoIter = T, Item = Result<bool, Box<dyn Error>>>,
|
|
>(
|
|
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<String>,
|
|
}
|
|
|
|
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<dyn Error>)],
|
|
),
|
|
false,
|
|
)
|
|
.unwrap_err();
|
|
assert_eq!(err.to_string(), "Dummy symbol error");
|
|
}
|
|
}
|