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( &self, runner: &R, logger: &L, force: bool, ) -> Result>; } #[async_trait(?Send)] #[allow(clippy::use_self)] impl Runnable for S where Self: Symbol + Debug, { async fn run( &self, runner: &R, logger: &L, force: bool, ) -> Result> { 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> { 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 { _target_reached: RefCell, _execute: RefCell, } #[async_trait(?Send)] impl< E: Iterator>>, T: Iterator>>, > Symbol for DummySymbol { async fn target_reached(&self) -> Result> { self._target_reached.borrow_mut().next().unwrap() } async fn execute(&self) -> Result<(), Box> { self._execute.borrow_mut().next().unwrap() } } impl< E: Iterator>>, T: Iterator>>, > DummySymbol { fn new< IE: IntoIterator>>, IT: IntoIterator>>, >( 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>, } fn get_runner() -> (Rc>, 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( &self, symbol: &S, _logger: &L, force: bool, ) -> Result> { 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>, Result>) { 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); } }