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.

195 lines
4.7 KiB

use super::SymbolRunner;
use crate::loggers::Logger;
use crate::symbols::Symbol;
use async_trait::async_trait;
use std::error::Error;
use std::fmt::Debug;
#[async_trait(?Send)]
pub trait Runnable {
async fn run<R: SymbolRunner, L: Logger>(
&self,
runner: &R,
logger: &L,
force: bool,
) -> Result<bool, Box<dyn Error>>;
}
#[async_trait(?Send)]
#[allow(clippy::use_self)]
impl<S> Runnable for S
where
Self: Symbol + Debug,
{
async fn run<R: SymbolRunner, L: Logger>(
&self,
runner: &R,
logger: &L,
force: bool,
) -> Result<bool, Box<dyn Error>> {
runner.run_symbol(self, logger, force).await
}
}
macro_rules! runnable_for_tuple {
( $($name:ident)* ) => (
#[allow(clippy::let_unit_value)]
#[async_trait(?Send)]
#[allow(non_snake_case)]
impl<$($name: Symbol + Debug,)*> Runnable for ($($name,)*) {
#[allow(unused)]
async fn run<_R: SymbolRunner, _L: Logger>(&self, runner: &_R, logger: &_L, force: bool) -> Result<bool, Box<dyn Error>> {
let ($($name,)*) = self;
let mut result = false;
$(result = runner.run_symbol($name, logger, force || result).await? || result;)*
Ok(result)
}
}
);
}
for_each_tuple!(runnable_for_tuple);
#[cfg(test)]
mod test {
use super::Runnable;
use crate::async_utils::run;
use crate::loggers::{Logger, StoringLogger};
use crate::symbols::Symbol;
use crate::SymbolRunner;
use async_trait::async_trait;
use std::cell::RefCell;
use std::error::Error;
use std::fmt::Debug;
use std::rc::Rc;
#[derive(Debug)]
struct DummySymbol<T, E> {
_target_reached: RefCell<T>,
_execute: RefCell<E>,
}
#[async_trait(?Send)]
impl<
E: Iterator<Item = Result<(), Box<dyn Error>>>,
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
> Symbol for DummySymbol<T, E>
{
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
self._target_reached.borrow_mut().next().unwrap()
}
async 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 TestSymbolRunner {
count: Rc<RefCell<usize>>,
}
fn get_runner() -> (Rc<RefCell<usize>>, TestSymbolRunner) {
let count = Rc::new(RefCell::new(0));
let runner = TestSymbolRunner {
count: Rc::clone(&count),
};
(count, runner)
}
#[async_trait(?Send)]
impl SymbolRunner for TestSymbolRunner {
async fn run_symbol<S: Symbol + Debug, L: Logger>(
&self,
symbol: &S,
_logger: &L,
force: bool,
) -> Result<bool, Box<dyn Error>> {
let run = force || !symbol.target_reached().await?;
if run {
*self.count.borrow_mut() += 1;
}
Ok(run)
}
}
fn run_symbol(
runnable: impl Runnable,
force: bool,
) -> (Rc<RefCell<usize>>, Result<bool, Box<dyn Error>>) {
let (count, runner) = get_runner();
let res = run(runnable.run(&runner, &StoringLogger::new(), force));
(count, res)
}
#[test]
fn correctly_handles_symbol_tuples() {
let (count, res) = run_symbol(
(
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
),
false,
);
res.unwrap();
assert_eq!(*count.borrow(), 2);
let (count, res) = run_symbol(
(
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
),
false,
);
res.unwrap();
assert_eq!(*count.borrow(), 1);
// An unreached symbol forces all further symbols
let (count, res) = run_symbol(
(
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
),
false,
);
res.unwrap();
assert_eq!(*count.borrow(), 2);
let (count, res) = run_symbol(
(
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
),
false,
);
res.unwrap();
assert_eq!(*count.borrow(), 0);
let (count, res) = run_symbol(
(
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
),
true,
);
res.unwrap();
assert_eq!(*count.borrow(), 2);
}
}