44 changed files with 2081 additions and 1242 deletions
			
			
		- 
					5Cargo.toml
 - 
					87src/async_utils.rs
 - 
					200src/builder.rs
 - 
					124src/command_runner.rs
 - 
					19src/lib.rs
 - 
					2src/locator.rs
 - 
					125src/loggers.rs
 - 
					43src/resources/mod.rs
 - 
					274src/schema.rs
 - 
					396src/setup.rs
 - 
					191src/setup/core.rs
 - 
					10src/setup/mod.rs
 - 
					193src/setup/runnable.rs
 - 
					300src/setup/setup.rs
 - 
					318src/setup/symbol_runner.rs
 - 
					9src/setup/util.rs
 - 
					61src/symbols/acme/cert.rs
 - 
					6src/symbols/concat.rs
 - 
					27src/symbols/cron.rs
 - 
					6src/symbols/dir.rs
 - 
					6src/symbols/file.rs
 - 
					124src/symbols/git/checkout.rs
 - 
					2src/symbols/git/mod.rs
 - 
					43src/symbols/git/submodules.rs
 - 
					43src/symbols/mariadb/database.rs
 - 
					43src/symbols/mariadb/dump.rs
 - 
					24src/symbols/mariadb/user.rs
 - 
					6src/symbols/mod.rs
 - 
					53src/symbols/npm.rs
 - 
					18src/symbols/owner.rs
 - 
					100src/symbols/postgresql/database.rs
 - 
					69src/symbols/saved_directory.rs
 - 
					18src/symbols/systemd/reload.rs
 - 
					61src/symbols/systemd/user_service.rs
 - 
					7src/symbols/systemd/user_session.rs
 - 
					49src/symbols/tls/csr.rs
 - 
					52src/symbols/tls/key.rs
 - 
					34src/symbols/user.rs
 - 
					57src/symbols/wordpress/plugin.rs
 - 
					28src/symbols/wordpress/translation.rs
 - 
					1src/templates/nginx/server.rs
 - 
					2src/to_artifact.rs
 - 
					57tests/file.rs
 - 
					30tests/setup.rs
 
@ -0,0 +1,87 @@ | 
			
		|||||
 | 
				use std::{
 | 
			
		||||
 | 
				  future::Future,
 | 
			
		||||
 | 
				  pin::Pin,
 | 
			
		||||
 | 
				  sync::{Arc, Mutex},
 | 
			
		||||
 | 
				  task::{Context, Poll, Waker},
 | 
			
		||||
 | 
				  thread,
 | 
			
		||||
 | 
				  time::Duration,
 | 
			
		||||
 | 
				};
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				pub use async_trait::async_trait;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				pub fn run<F: Future>(future: F) -> F::Output {
 | 
			
		||||
 | 
				  tokio::runtime::Runtime::new().unwrap().block_on(future)
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				pub use tokio::try_join;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Debug)]
 | 
			
		||||
 | 
				pub struct TimerFuture {
 | 
			
		||||
 | 
				  state: Arc<Mutex<State>>,
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Debug)]
 | 
			
		||||
 | 
				enum State {
 | 
			
		||||
 | 
				  NotStarted(Duration),
 | 
			
		||||
 | 
				  Running(Waker),
 | 
			
		||||
 | 
				  Completed,
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				impl Future for TimerFuture {
 | 
			
		||||
 | 
				  type Output = ();
 | 
			
		||||
 | 
				  fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
 | 
			
		||||
 | 
				    let mut state = self.state.lock().unwrap();
 | 
			
		||||
 | 
				    if let State::Completed = *state {
 | 
			
		||||
 | 
				      return Poll::Ready(());
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    if let State::NotStarted(duration) = *state {
 | 
			
		||||
 | 
				      let thread_state = self.state.clone();
 | 
			
		||||
 | 
				      thread::spawn(move || {
 | 
			
		||||
 | 
				        thread::sleep(duration);
 | 
			
		||||
 | 
				        let mut state = thread_state.lock().unwrap();
 | 
			
		||||
 | 
				        let waker = if let State::Running(waker) = &*state {
 | 
			
		||||
 | 
				          Some(waker.clone())
 | 
			
		||||
 | 
				        } else {
 | 
			
		||||
 | 
				          None
 | 
			
		||||
 | 
				        };
 | 
			
		||||
 | 
				        *state = State::Completed;
 | 
			
		||||
 | 
				        if let Some(w) = waker {
 | 
			
		||||
 | 
				          w.wake()
 | 
			
		||||
 | 
				        }
 | 
			
		||||
 | 
				      });
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    *state = State::Running(cx.waker().clone());
 | 
			
		||||
 | 
				    Poll::Pending
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				pub fn sleep(duration: Duration) -> impl Future<Output = ()> {
 | 
			
		||||
 | 
				  TimerFuture {
 | 
			
		||||
 | 
				    state: Arc::new(Mutex::new(State::NotStarted(duration))),
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[cfg(test)]
 | 
			
		||||
 | 
				mod test {
 | 
			
		||||
 | 
				  use crate::async_utils::{run, sleep};
 | 
			
		||||
 | 
				  use futures::future::FutureExt;
 | 
			
		||||
 | 
				  use std::time::{Duration, Instant};
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[test]
 | 
			
		||||
 | 
				  fn test_sleep() {
 | 
			
		||||
 | 
				    run(async {
 | 
			
		||||
 | 
				      let start = Instant::now();
 | 
			
		||||
 | 
				      let sleep = sleep(Duration::from_millis(100)).fuse();
 | 
			
		||||
 | 
				      let ok = async {}.fuse();
 | 
			
		||||
 | 
				      futures::pin_mut!(sleep, ok);
 | 
			
		||||
 | 
				      loop {
 | 
			
		||||
 | 
				        futures::select! {
 | 
			
		||||
 | 
				          _ = sleep => {},
 | 
			
		||||
 | 
				          _ = ok => assert!((Instant::now() - start).as_millis() < 100),
 | 
			
		||||
 | 
				          complete => break,
 | 
			
		||||
 | 
				        }
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				    })
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
@ -1,274 +0,0 @@ | 
			
		|||||
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");
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
@ -1,396 +0,0 @@ | 
			
		|||||
use crate::resources::{BorrowResource, DefaultResources, Resource};
 | 
				 | 
			
		||||
use crate::schema::SymbolRunner;
 | 
				 | 
			
		||||
use crate::symbols::Symbol;
 | 
				 | 
			
		||||
use crate::to_artifact::ToArtifact;
 | 
				 | 
			
		||||
use crate::{DefaultBuilder, DefaultLocator, ResourceLocator, SymbolBuilder};
 | 
				 | 
			
		||||
use std::collections::HashSet;
 | 
				 | 
			
		||||
use std::error::Error;
 | 
				 | 
			
		||||
use std::fmt::Debug;
 | 
				 | 
			
		||||
use std::hash::Hash;
 | 
				 | 
			
		||||
use std::marker::PhantomData;
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
pub trait CanHandle<X: ToArtifact> {
 | 
				 | 
			
		||||
  fn handle(&mut self, x: X) -> Result<(X::Artifact, bool), Box<dyn Error>>;
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
macro_rules! can_handle {
 | 
				 | 
			
		||||
  ( $($name:ident)* ) => (
 | 
				 | 
			
		||||
    #[allow(non_snake_case)]
 | 
				 | 
			
		||||
    impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, $($name: Resource,)*>
 | 
				 | 
			
		||||
      CanHandle<($($name,)*)>
 | 
				 | 
			
		||||
      for Setup<_SR, _L, _R, _B>
 | 
				 | 
			
		||||
      where
 | 
				 | 
			
		||||
      $(
 | 
				 | 
			
		||||
        _B: SymbolBuilder<$name>,
 | 
				 | 
			
		||||
        <_B as SymbolBuilder<$name>>::Symbol: Runnable + Debug,
 | 
				 | 
			
		||||
        _L: ResourceLocator<$name>,
 | 
				 | 
			
		||||
        _R: From<$name> + BorrowResource<$name>,
 | 
				 | 
			
		||||
        Self: CanHandle<<_B as SymbolBuilder<$name>>::Prerequisites> +
 | 
				 | 
			
		||||
          CanHandle<<_L as ResourceLocator<$name>>::Prerequisites>
 | 
				 | 
			
		||||
      ),*
 | 
				 | 
			
		||||
    {
 | 
				 | 
			
		||||
      fn handle(&mut self, ($($name,)*): ($($name,)*)) -> Result<(($($name::Artifact,)*), bool), Box<dyn Error>>
 | 
				 | 
			
		||||
      {
 | 
				 | 
			
		||||
        $(let $name = self.add($name)?;)*
 | 
				 | 
			
		||||
        Ok((($($name.0,)*), false $(|| $name.1)*))
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  );
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
for_each_tuple!(can_handle);
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
// This is for self-referential T
 | 
				 | 
			
		||||
// FIXME: Wait for specialization
 | 
				 | 
			
		||||
impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<Option<T>>
 | 
				 | 
			
		||||
  for Setup<_SR, _L, _R, _B>
 | 
				 | 
			
		||||
where
 | 
				 | 
			
		||||
  _B: SymbolBuilder<T>,
 | 
				 | 
			
		||||
  <_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
 | 
				 | 
			
		||||
  _L: ResourceLocator<T, Prerequisites = Option<T>>,
 | 
				 | 
			
		||||
  _R: From<T> + BorrowResource<T>,
 | 
				 | 
			
		||||
  Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>,
 | 
				 | 
			
		||||
{
 | 
				 | 
			
		||||
  fn handle(
 | 
				 | 
			
		||||
    &mut self,
 | 
				 | 
			
		||||
    r: Option<T>,
 | 
				 | 
			
		||||
  ) -> Result<(<Option<T> as ToArtifact>::Artifact, bool), Box<dyn Error>> {
 | 
				 | 
			
		||||
    Ok(match r {
 | 
				 | 
			
		||||
      Some(r) => {
 | 
				 | 
			
		||||
        let (result, did_run) = self.add(r)?;
 | 
				 | 
			
		||||
        (Some(result), did_run)
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
      None => (None, false),
 | 
				 | 
			
		||||
    })
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<T> for Setup<_SR, _L, _R, _B>
 | 
				 | 
			
		||||
where
 | 
				 | 
			
		||||
  _B: SymbolBuilder<T>,
 | 
				 | 
			
		||||
  <_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
 | 
				 | 
			
		||||
  _L: ResourceLocator<T>,
 | 
				 | 
			
		||||
  _R: From<T> + Debug + BorrowResource<T>,
 | 
				 | 
			
		||||
  Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>
 | 
				 | 
			
		||||
    + CanHandle<<_L as ResourceLocator<T>>::Prerequisites>,
 | 
				 | 
			
		||||
{
 | 
				 | 
			
		||||
  fn handle(&mut self, r: T) -> Result<(<T as ToArtifact>::Artifact, bool), Box<dyn Error>> {
 | 
				 | 
			
		||||
    self.add(r)
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
pub struct Setup<
 | 
				 | 
			
		||||
  SR,
 | 
				 | 
			
		||||
  L = DefaultLocator,
 | 
				 | 
			
		||||
  R = DefaultResources<'static, &'static str>,
 | 
				 | 
			
		||||
  B = DefaultBuilder,
 | 
				 | 
			
		||||
> {
 | 
				 | 
			
		||||
  symbol_runner: SR,
 | 
				 | 
			
		||||
  resources: HashSet<R>,
 | 
				 | 
			
		||||
  phantom: PhantomData<(L, B)>,
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
// https://github.com/rust-lang/rust/issues/27336
 | 
				 | 
			
		||||
impl<SR> Setup<SR> {
 | 
				 | 
			
		||||
  pub fn new(symbol_runner: SR) -> Self {
 | 
				 | 
			
		||||
    Self {
 | 
				 | 
			
		||||
      symbol_runner,
 | 
				 | 
			
		||||
      resources: Default::default(),
 | 
				 | 
			
		||||
      phantom: Default::default(),
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
impl<SR, L, R: Hash + Eq, B> Setup<SR, L, R, B> {
 | 
				 | 
			
		||||
  pub fn new_with(symbol_runner: SR) -> Self {
 | 
				 | 
			
		||||
    Self {
 | 
				 | 
			
		||||
      symbol_runner,
 | 
				 | 
			
		||||
      resources: Default::default(),
 | 
				 | 
			
		||||
      phantom: Default::default(),
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
pub trait Runnable {
 | 
				 | 
			
		||||
  fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>>;
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
impl<S: Symbol + Debug> Runnable for S {
 | 
				 | 
			
		||||
  fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>> {
 | 
				 | 
			
		||||
    runner.run_symbol(self, force)
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
macro_rules! runnable_for_tuple {
 | 
				 | 
			
		||||
  ( $($name:ident)* ) => (
 | 
				 | 
			
		||||
    #[allow(non_snake_case)]
 | 
				 | 
			
		||||
    impl<$($name: Symbol + Debug,)*> Runnable for ($($name,)*) {
 | 
				 | 
			
		||||
      #[allow(unused)]
 | 
				 | 
			
		||||
      fn run<_R: SymbolRunner>(&self, runner: &_R, force: bool) -> Result<bool, Box<dyn Error>> {
 | 
				 | 
			
		||||
        let ($($name,)*) = self;
 | 
				 | 
			
		||||
        let mut result = false;
 | 
				 | 
			
		||||
        $(result = runner.run_symbol($name, force || result)? || result;)*
 | 
				 | 
			
		||||
        Ok(result)
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  );
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
for_each_tuple!(runnable_for_tuple);
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
impl<SR: SymbolRunner, L, Rs: Hash + Eq, B> Setup<SR, L, Rs, B> {
 | 
				 | 
			
		||||
  pub fn add<R: Resource>(&mut self, resource: R) -> Result<(R::Artifact, bool), Box<dyn Error>>
 | 
				 | 
			
		||||
  where
 | 
				 | 
			
		||||
    B: SymbolBuilder<R>,
 | 
				 | 
			
		||||
    <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
 | 
				 | 
			
		||||
    L: ResourceLocator<R>,
 | 
				 | 
			
		||||
    Rs: From<R> + BorrowResource<R>,
 | 
				 | 
			
		||||
    Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
 | 
				 | 
			
		||||
  {
 | 
				 | 
			
		||||
    self.add_force(resource, false)
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  pub fn add_force<R: Resource>(
 | 
				 | 
			
		||||
    &mut self,
 | 
				 | 
			
		||||
    resource: R,
 | 
				 | 
			
		||||
    force_run: bool,
 | 
				 | 
			
		||||
  ) -> Result<(R::Artifact, bool), Box<dyn Error>>
 | 
				 | 
			
		||||
  where
 | 
				 | 
			
		||||
    B: SymbolBuilder<R>,
 | 
				 | 
			
		||||
    <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
 | 
				 | 
			
		||||
    L: ResourceLocator<R>,
 | 
				 | 
			
		||||
    Rs: From<R> + BorrowResource<R>,
 | 
				 | 
			
		||||
    Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
 | 
				 | 
			
		||||
  {
 | 
				 | 
			
		||||
    let (target, target_prereqs) = L::locate(&resource);
 | 
				 | 
			
		||||
    let storable_resource = Rs::from(resource);
 | 
				 | 
			
		||||
    let did_run = if self.resources.get(&storable_resource).is_some() {
 | 
				 | 
			
		||||
      assert!(
 | 
				 | 
			
		||||
        !force_run,
 | 
				 | 
			
		||||
        "Forcing to run an already-added resource is a logical error"
 | 
				 | 
			
		||||
      );
 | 
				 | 
			
		||||
      false
 | 
				 | 
			
		||||
    } else {
 | 
				 | 
			
		||||
      let (_, target_prereqs_did_run) = self.handle(target_prereqs)?;
 | 
				 | 
			
		||||
      let (symbol, prereqs_did_run) =
 | 
				 | 
			
		||||
        self.get_symbol(storable_resource.borrow_resource().unwrap(), &target)?;
 | 
				 | 
			
		||||
      self.resources.insert(storable_resource);
 | 
				 | 
			
		||||
      self.run_symbol(
 | 
				 | 
			
		||||
        symbol,
 | 
				 | 
			
		||||
        force_run || target_prereqs_did_run || prereqs_did_run,
 | 
				 | 
			
		||||
      )?
 | 
				 | 
			
		||||
    };
 | 
				 | 
			
		||||
    Ok((target, did_run))
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  fn get_symbol<R: Resource>(
 | 
				 | 
			
		||||
    &mut self,
 | 
				 | 
			
		||||
    resource: &R,
 | 
				 | 
			
		||||
    target: &R::Artifact,
 | 
				 | 
			
		||||
  ) -> Result<(<B as SymbolBuilder<R>>::Symbol, bool), Box<dyn Error>>
 | 
				 | 
			
		||||
  where
 | 
				 | 
			
		||||
    B: SymbolBuilder<R>,
 | 
				 | 
			
		||||
    Self: CanHandle<B::Prerequisites>,
 | 
				 | 
			
		||||
  {
 | 
				 | 
			
		||||
    let (prereqs, prereqs_did_run) = self.handle(B::prerequisites(resource))?;
 | 
				 | 
			
		||||
    Ok((B::create(resource, target, prereqs), prereqs_did_run))
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  pub fn run_symbol<S: Runnable>(&self, symbol: S, force: bool) -> Result<bool, Box<dyn Error>> {
 | 
				 | 
			
		||||
    symbol.run(&self.symbol_runner, force)
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
#[cfg(test)]
 | 
				 | 
			
		||||
mod test {
 | 
				 | 
			
		||||
  use crate::resources::{BorrowResource, Resource};
 | 
				 | 
			
		||||
  use crate::schema::SymbolRunner;
 | 
				 | 
			
		||||
  use crate::symbols::Symbol;
 | 
				 | 
			
		||||
  use crate::to_artifact::ToArtifact;
 | 
				 | 
			
		||||
  use crate::{ResourceLocator, Setup, SymbolBuilder};
 | 
				 | 
			
		||||
  use std::cell::RefCell;
 | 
				 | 
			
		||||
  use std::error::Error;
 | 
				 | 
			
		||||
  use std::fmt::Debug;
 | 
				 | 
			
		||||
  use std::rc::Rc;
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  struct TestSymbolRunner {
 | 
				 | 
			
		||||
    count: Rc<RefCell<usize>>,
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  impl SymbolRunner for TestSymbolRunner {
 | 
				 | 
			
		||||
    fn run_symbol<S: Symbol + Debug>(
 | 
				 | 
			
		||||
      &self,
 | 
				 | 
			
		||||
      symbol: &S,
 | 
				 | 
			
		||||
      force: bool,
 | 
				 | 
			
		||||
    ) -> Result<bool, Box<dyn Error>> {
 | 
				 | 
			
		||||
      let run = force || !symbol.target_reached()?;
 | 
				 | 
			
		||||
      if run {
 | 
				 | 
			
		||||
        *self.count.borrow_mut() += 1;
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
      Ok(run)
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  #[derive(Debug, PartialEq, Eq, Hash)]
 | 
				 | 
			
		||||
  struct TestResource<T>(&'static str, T);
 | 
				 | 
			
		||||
  impl<T> Resource for TestResource<T> {
 | 
				 | 
			
		||||
    type Artifact = ();
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  #[derive(Debug, Hash, PartialEq, Eq)]
 | 
				 | 
			
		||||
  enum Resources {
 | 
				 | 
			
		||||
    A(TestResource<&'static str>),
 | 
				 | 
			
		||||
    B(TestResource<()>),
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
  impl From<TestResource<&'static str>> for Resources {
 | 
				 | 
			
		||||
    fn from(from: TestResource<&'static str>) -> Self {
 | 
				 | 
			
		||||
      Self::A(from)
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
  impl From<TestResource<()>> for Resources {
 | 
				 | 
			
		||||
    fn from(from: TestResource<()>) -> Self {
 | 
				 | 
			
		||||
      Self::B(from)
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  impl BorrowResource<TestResource<&'static str>> for Resources {
 | 
				 | 
			
		||||
    fn borrow_resource(&self) -> Option<&TestResource<&'static str>> {
 | 
				 | 
			
		||||
      match self {
 | 
				 | 
			
		||||
        Self::A(a) => Some(a),
 | 
				 | 
			
		||||
        _ => None,
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
  impl BorrowResource<TestResource<()>> for Resources {
 | 
				 | 
			
		||||
    fn borrow_resource(&self) -> Option<&TestResource<()>> {
 | 
				 | 
			
		||||
      match self {
 | 
				 | 
			
		||||
        Self::B(b) => Some(b),
 | 
				 | 
			
		||||
        _ => None,
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  struct TestResourceLocator;
 | 
				 | 
			
		||||
  impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
 | 
				 | 
			
		||||
    type Prerequisites = ();
 | 
				 | 
			
		||||
    fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
 | 
				 | 
			
		||||
      ((), ())
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  struct TestSymbolBuilder;
 | 
				 | 
			
		||||
  impl SymbolBuilder<TestResource<&'static str>> for TestSymbolBuilder {
 | 
				 | 
			
		||||
    type Symbol = TestSymbol;
 | 
				 | 
			
		||||
    type Prerequisites = TestResource<()>;
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
 | 
				 | 
			
		||||
      TestResource(resource.1, ())
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
    fn create(
 | 
				 | 
			
		||||
      resource: &TestResource<&'static str>,
 | 
				 | 
			
		||||
      (): &(),
 | 
				 | 
			
		||||
      _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				 | 
			
		||||
    ) -> Self::Symbol {
 | 
				 | 
			
		||||
      TestSymbol {
 | 
				 | 
			
		||||
        reached: resource.0.chars().next().unwrap().is_uppercase(),
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
  impl SymbolBuilder<TestResource<()>> for TestSymbolBuilder {
 | 
				 | 
			
		||||
    type Symbol = TestSymbol;
 | 
				 | 
			
		||||
    type Prerequisites = ();
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
 | 
				 | 
			
		||||
    fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Symbol {
 | 
				 | 
			
		||||
      TestSymbol {
 | 
				 | 
			
		||||
        reached: resource.0.chars().next().unwrap().is_uppercase(),
 | 
				 | 
			
		||||
      }
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  #[derive(Debug)]
 | 
				 | 
			
		||||
  struct TestSymbol {
 | 
				 | 
			
		||||
    reached: bool,
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
  impl Symbol for TestSymbol {
 | 
				 | 
			
		||||
    fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				 | 
			
		||||
      Ok(self.reached)
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
    fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				 | 
			
		||||
      Ok(())
 | 
				 | 
			
		||||
    }
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  fn get_setup() -> (
 | 
				 | 
			
		||||
    Rc<RefCell<usize>>,
 | 
				 | 
			
		||||
    Setup<TestSymbolRunner, TestResourceLocator, Resources, TestSymbolBuilder>,
 | 
				 | 
			
		||||
  ) {
 | 
				 | 
			
		||||
    let count = Rc::new(RefCell::new(0));
 | 
				 | 
			
		||||
    let runner = TestSymbolRunner {
 | 
				 | 
			
		||||
      count: Rc::clone(&count),
 | 
				 | 
			
		||||
    };
 | 
				 | 
			
		||||
    (count, Setup::new_with(runner))
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  #[test]
 | 
				 | 
			
		||||
  fn correctly_uses_force() {
 | 
				 | 
			
		||||
    let (count, mut setup) = get_setup();
 | 
				 | 
			
		||||
    setup.add(TestResource("A", "b")).unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 2);
 | 
				 | 
			
		||||
    setup.add(TestResource("A", "b")).unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 2);
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    let (count, mut setup) = get_setup();
 | 
				 | 
			
		||||
    setup.add(TestResource("A", "B")).unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 0);
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  #[test]
 | 
				 | 
			
		||||
  fn correctly_handles_symbol_tuples() {
 | 
				 | 
			
		||||
    let (count, setup) = get_setup();
 | 
				 | 
			
		||||
    setup
 | 
				 | 
			
		||||
      .run_symbol(
 | 
				 | 
			
		||||
        (TestSymbol { reached: false }, TestSymbol { reached: false }),
 | 
				 | 
			
		||||
        false,
 | 
				 | 
			
		||||
      )
 | 
				 | 
			
		||||
      .unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 2);
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    let (count, setup) = get_setup();
 | 
				 | 
			
		||||
    setup
 | 
				 | 
			
		||||
      .run_symbol(
 | 
				 | 
			
		||||
        (TestSymbol { reached: true }, TestSymbol { reached: false }),
 | 
				 | 
			
		||||
        false,
 | 
				 | 
			
		||||
      )
 | 
				 | 
			
		||||
      .unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 1);
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    // An unreached symbol forces all further symbols
 | 
				 | 
			
		||||
    let (count, setup) = get_setup();
 | 
				 | 
			
		||||
    setup
 | 
				 | 
			
		||||
      .run_symbol(
 | 
				 | 
			
		||||
        (TestSymbol { reached: false }, TestSymbol { reached: true }),
 | 
				 | 
			
		||||
        false,
 | 
				 | 
			
		||||
      )
 | 
				 | 
			
		||||
      .unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 2);
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    let (count, setup) = get_setup();
 | 
				 | 
			
		||||
    setup
 | 
				 | 
			
		||||
      .run_symbol(
 | 
				 | 
			
		||||
        (TestSymbol { reached: true }, TestSymbol { reached: true }),
 | 
				 | 
			
		||||
        false,
 | 
				 | 
			
		||||
      )
 | 
				 | 
			
		||||
      .unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 0);
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    let (count, setup) = get_setup();
 | 
				 | 
			
		||||
    setup
 | 
				 | 
			
		||||
      .run_symbol(
 | 
				 | 
			
		||||
        (TestSymbol { reached: true }, TestSymbol { reached: true }),
 | 
				 | 
			
		||||
        true,
 | 
				 | 
			
		||||
      )
 | 
				 | 
			
		||||
      .unwrap();
 | 
				 | 
			
		||||
    assert_eq!(*count.borrow(), 2);
 | 
				 | 
			
		||||
  }
 | 
				 | 
			
		||||
}
 | 
				 | 
			
		||||
@ -0,0 +1,191 @@ | 
			
		|||||
 | 
				use super::runnable::Runnable;
 | 
			
		||||
 | 
				use super::util::{AddResult, AddableResource};
 | 
			
		||||
 | 
				use super::Setup;
 | 
			
		||||
 | 
				use super::SymbolRunner;
 | 
			
		||||
 | 
				use crate::async_utils::try_join;
 | 
			
		||||
 | 
				use crate::loggers::{Logger, StoringLogger};
 | 
			
		||||
 | 
				use crate::resources::{FromArtifact, FromResource};
 | 
			
		||||
 | 
				use crate::symbols::Symbol;
 | 
			
		||||
 | 
				use crate::to_artifact::ToArtifact;
 | 
			
		||||
 | 
				use crate::{ImplementationBuilder, ResourceLocator};
 | 
			
		||||
 | 
				use async_trait::async_trait;
 | 
			
		||||
 | 
				use std::error::Error;
 | 
			
		||||
 | 
				use std::fmt::Debug;
 | 
			
		||||
 | 
				use std::hash::Hash;
 | 
			
		||||
 | 
				use std::marker::PhantomData;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				pub trait AddGeneric<X: ToArtifact> {
 | 
			
		||||
 | 
				  async fn add_generic(&self, x: X) -> AddResult<X>;
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				macro_rules! add_generic {
 | 
			
		||||
 | 
				  ( $($name:ident)* ) => (
 | 
			
		||||
 | 
				    #[async_trait(?Send)]
 | 
			
		||||
 | 
				    #[allow(non_snake_case)]
 | 
			
		||||
 | 
				    impl<SR: 'static, _L: 'static, _B: 'static, LOG: 'static + Logger, Rs: 'static + Hash + Eq, As: 'static + Clone, $($name: AddableResource,)*>
 | 
			
		||||
 | 
				      AddGeneric<($($name,)*)> for Setup<SR, LOG, _L, _B, Rs, As>
 | 
			
		||||
 | 
				      where
 | 
			
		||||
 | 
				      $(
 | 
			
		||||
 | 
				        RegularSetupCore<SR, _L, _B>: SetupCore<$name, Self>,
 | 
			
		||||
 | 
				        As: FromArtifact<$name>,
 | 
			
		||||
 | 
				        Rs: FromResource<$name>,
 | 
			
		||||
 | 
				        $name::Artifact: Clone | 
			
		||||
 | 
				      ),*
 | 
			
		||||
 | 
				    {
 | 
			
		||||
 | 
				      #[allow(unused)]
 | 
			
		||||
 | 
				      async fn add_generic(&self, ($($name,)*): ($($name,)*)) -> Result<(($($name::Artifact,)*), bool), Box<dyn Error>>
 | 
			
		||||
 | 
				      {
 | 
			
		||||
 | 
				        let x: Result<_, Box<dyn Error>> = try_join!($(self.add_async($name, false),)*);
 | 
			
		||||
 | 
				        let ($($name,)*) = x?;
 | 
			
		||||
 | 
				        Ok((($($name.0,)*), false $(|| $name.1)*))
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  );
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				for_each_tuple!(add_generic);
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				// This is for self-referential T
 | 
			
		||||
 | 
				// FIXME: Wait for specialization
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				impl<
 | 
			
		||||
 | 
				    SR: 'static + SymbolRunner,
 | 
			
		||||
 | 
				    LOG: 'static + Logger,
 | 
			
		||||
 | 
				    T: AddableResource,
 | 
			
		||||
 | 
				    Rs: 'static + Hash + Eq + FromResource<T>,
 | 
			
		||||
 | 
				    As: 'static + FromArtifact<T> + Clone,
 | 
			
		||||
 | 
				    L: 'static + ResourceLocator<T, Prerequisites = Option<T>>,
 | 
			
		||||
 | 
				    B: 'static + ImplementationBuilder<T>,
 | 
			
		||||
 | 
				  > AddGeneric<Option<T>> for Setup<SR, LOG, L, B, Rs, As>
 | 
			
		||||
 | 
				where
 | 
			
		||||
 | 
				  <B as ImplementationBuilder<T>>::Implementation: Runnable + Debug,
 | 
			
		||||
 | 
				  Self: AddGeneric<B::Prerequisites>,
 | 
			
		||||
 | 
				  T::Artifact: Clone,
 | 
			
		||||
 | 
				{
 | 
			
		||||
 | 
				  async fn add_generic(&self, r: Option<T>) -> AddResult<Option<T>> {
 | 
			
		||||
 | 
				    Ok(match r {
 | 
			
		||||
 | 
				      Some(r) => {
 | 
			
		||||
 | 
				        let (result, did_run) = self.add_async(r, false).await?;
 | 
			
		||||
 | 
				        (Some(result), did_run)
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				      None => (None, false),
 | 
			
		||||
 | 
				    })
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				impl<
 | 
			
		||||
 | 
				    LOG: 'static + Logger,
 | 
			
		||||
 | 
				    T: AddableResource,
 | 
			
		||||
 | 
				    Rs: 'static + Hash + Eq + FromResource<T>,
 | 
			
		||||
 | 
				    As: 'static + FromArtifact<T> + Clone,
 | 
			
		||||
 | 
				    SR: 'static,
 | 
			
		||||
 | 
				    L: 'static,
 | 
			
		||||
 | 
				    B: 'static,
 | 
			
		||||
 | 
				  > AddGeneric<T> for Setup<SR, LOG, L, B, Rs, As>
 | 
			
		||||
 | 
				where
 | 
			
		||||
 | 
				  T::Artifact: Clone,
 | 
			
		||||
 | 
				  RegularSetupCore<SR, L, B>: 'static + SetupCore<T, Self>,
 | 
			
		||||
 | 
				{
 | 
			
		||||
 | 
				  async fn add_generic(&self, r: T) -> AddResult<T> {
 | 
			
		||||
 | 
				    self.add_async(r, false).await
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				pub trait SetupCore<R: AddableResource, S> {
 | 
			
		||||
 | 
				  async fn add<LOG: Logger, RR: AsRef<R>>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    setup: &S,
 | 
			
		||||
 | 
				    parent_logger: &LOG,
 | 
			
		||||
 | 
				    resource: RR,
 | 
			
		||||
 | 
				    force_run: bool,
 | 
			
		||||
 | 
				  ) -> AddResult<R>;
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Debug)]
 | 
			
		||||
 | 
				pub struct RegularSetupCore<SR, L, B> {
 | 
			
		||||
 | 
				  symbol_runner: SR,
 | 
			
		||||
 | 
				  phantom: PhantomData<(L, B)>,
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				impl<SR, L, B> RegularSetupCore<SR, L, B> {
 | 
			
		||||
 | 
				  pub fn new(symbol_runner: SR) -> Self {
 | 
			
		||||
 | 
				    Self {
 | 
			
		||||
 | 
				      symbol_runner,
 | 
			
		||||
 | 
				      phantom: PhantomData::default(),
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				impl<SR: SymbolRunner, L, B, R: AddableResource, S> SetupCore<R, S> for RegularSetupCore<SR, L, B>
 | 
			
		||||
 | 
				where
 | 
			
		||||
 | 
				  B: ImplementationBuilder<R>,
 | 
			
		||||
 | 
				  <B as ImplementationBuilder<R>>::Implementation: Runnable + Debug,
 | 
			
		||||
 | 
				  L: ResourceLocator<R>,
 | 
			
		||||
 | 
				  S: AddGeneric<B::Prerequisites> + AddGeneric<<L as ResourceLocator<R>>::Prerequisites>,
 | 
			
		||||
 | 
				{
 | 
			
		||||
 | 
				  async fn add<LOG: Logger, RR: AsRef<R>>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    setup: &S,
 | 
			
		||||
 | 
				    parent_logger: &LOG,
 | 
			
		||||
 | 
				    resource: RR,
 | 
			
		||||
 | 
				    force_run: bool,
 | 
			
		||||
 | 
				  ) -> AddResult<R> {
 | 
			
		||||
 | 
				    let resource = resource.as_ref();
 | 
			
		||||
 | 
				    let logger = StoringLogger::new();
 | 
			
		||||
 | 
				    logger.write(4, format!("Adding {:?} ... ", resource));
 | 
			
		||||
 | 
				    let result = {
 | 
			
		||||
 | 
				      logger.trace(format!(" (force_run is {})", force_run));
 | 
			
		||||
 | 
				      let (location, location_prereqs) = L::locate(resource);
 | 
			
		||||
 | 
				      logger.trace(format!("Adding location prereqs for {:?}", resource));
 | 
			
		||||
 | 
				      let (_, location_prereqs_did_run) = setup.add_generic(location_prereqs).await?;
 | 
			
		||||
 | 
				      logger.trace(format!(
 | 
			
		||||
 | 
				        "Location prereqs for {:?} did_run: {}",
 | 
			
		||||
 | 
				        resource, location_prereqs_did_run
 | 
			
		||||
 | 
				      ));
 | 
			
		||||
 | 
				      logger.trace(format!("Adding implementation prereqs for {:?}", resource));
 | 
			
		||||
 | 
				      let (prereqs, prereqs_did_run) = setup.add_generic(B::prerequisites(resource)).await?;
 | 
			
		||||
 | 
				      logger.trace(format!(
 | 
			
		||||
 | 
				        "Implementation prereqs for {:?} did_run: {}",
 | 
			
		||||
 | 
				        resource, prereqs_did_run
 | 
			
		||||
 | 
				      ));
 | 
			
		||||
 | 
				      logger.trace(format!("Running implementation for {:?}", resource));
 | 
			
		||||
 | 
				      let implementation = B::create(resource, &location, prereqs);
 | 
			
		||||
 | 
				      let did_run = implementation
 | 
			
		||||
 | 
				        .run(
 | 
			
		||||
 | 
				          &self.symbol_runner,
 | 
			
		||||
 | 
				          &logger,
 | 
			
		||||
 | 
				          force_run || location_prereqs_did_run || prereqs_did_run,
 | 
			
		||||
 | 
				        )
 | 
			
		||||
 | 
				        .await?;
 | 
			
		||||
 | 
				      Ok((location, did_run))
 | 
			
		||||
 | 
				    };
 | 
			
		||||
 | 
				    logger.write(4, "done.");
 | 
			
		||||
 | 
				    let max_level = if result.is_err() { 5 } else { 3 };
 | 
			
		||||
 | 
				    if parent_logger.put(logger.release().into_iter().filter(|e| e.0 <= max_level)) == 0 {
 | 
			
		||||
 | 
				      parent_logger.write(3, ".");
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				    result
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				impl<SR: SymbolRunner, L, B> SymbolRunner for RegularSetupCore<SR, L, B> {
 | 
			
		||||
 | 
				  async fn run_symbol<S: Symbol + Debug, LOG: Logger>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    symbol: &S,
 | 
			
		||||
 | 
				    parent_logger: &LOG,
 | 
			
		||||
 | 
				    force: bool,
 | 
			
		||||
 | 
				  ) -> Result<bool, Box<dyn Error>> {
 | 
			
		||||
 | 
				    let logger = StoringLogger::new();
 | 
			
		||||
 | 
				    logger.debug(format!("Directly running {:?} ...", symbol));
 | 
			
		||||
 | 
				    let result = self.symbol_runner.run_symbol(symbol, &logger, force).await;
 | 
			
		||||
 | 
				    logger.debug("done.");
 | 
			
		||||
 | 
				    let max_level = if result.is_err() { 5 } else { 3 };
 | 
			
		||||
 | 
				    parent_logger.put(logger.release().into_iter().filter(|e| e.0 <= max_level));
 | 
			
		||||
 | 
				    result
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
@ -0,0 +1,10 @@ | 
			
		|||||
 | 
				mod core;
 | 
			
		||||
 | 
				mod util;
 | 
			
		||||
 | 
				pub use util::{AddResult, AddableResource};
 | 
			
		||||
 | 
				mod symbol_runner;
 | 
			
		||||
 | 
				pub use symbol_runner::{
 | 
			
		||||
 | 
				  DrySymbolRunner, InitializingSymbolRunner, ReportingSymbolRunner, SymbolRunner,
 | 
			
		||||
 | 
				};
 | 
			
		||||
 | 
				mod runnable;
 | 
			
		||||
 | 
				mod setup;
 | 
			
		||||
 | 
				pub use setup::Setup;
 | 
			
		||||
@ -0,0 +1,193 @@ | 
			
		|||||
 | 
				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)]
 | 
			
		||||
 | 
				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)* ) => (
 | 
			
		||||
 | 
				    #[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);
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
@ -0,0 +1,300 @@ | 
			
		|||||
 | 
				use super::core::{RegularSetupCore, SetupCore};
 | 
			
		||||
 | 
				use super::runnable::Runnable;
 | 
			
		||||
 | 
				use super::util::{AddResult, AddableResource};
 | 
			
		||||
 | 
				use super::SymbolRunner;
 | 
			
		||||
 | 
				use crate::async_utils::run;
 | 
			
		||||
 | 
				use crate::loggers::Logger;
 | 
			
		||||
 | 
				use crate::resources::{DefaultArtifacts, DefaultResources, FromArtifact, FromResource};
 | 
			
		||||
 | 
				use crate::{DefaultBuilder, DefaultLocator};
 | 
			
		||||
 | 
				use futures::future::FutureExt;
 | 
			
		||||
 | 
				use futures::future::Shared;
 | 
			
		||||
 | 
				use std::cell::{RefCell, RefMut};
 | 
			
		||||
 | 
				use std::collections::HashMap;
 | 
			
		||||
 | 
				use std::error::Error;
 | 
			
		||||
 | 
				use std::future::Future;
 | 
			
		||||
 | 
				use std::hash::Hash;
 | 
			
		||||
 | 
				use std::pin::Pin;
 | 
			
		||||
 | 
				use std::rc::Rc;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				type Cache<Rs, As> = HashMap<Rs, Shared<Pin<Box<dyn Future<Output = (As, bool)>>>>>;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Debug)]
 | 
			
		||||
 | 
				struct SetupInner<CORE, LOG, Rs, As> {
 | 
			
		||||
 | 
				  core: CORE,
 | 
			
		||||
 | 
				  logger: LOG,
 | 
			
		||||
 | 
				  resources: RefCell<Cache<Rs, As>>,
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Debug)]
 | 
			
		||||
 | 
				pub struct Setup<
 | 
			
		||||
 | 
				  SR,
 | 
			
		||||
 | 
				  LOG,
 | 
			
		||||
 | 
				  L = DefaultLocator,
 | 
			
		||||
 | 
				  B = DefaultBuilder,
 | 
			
		||||
 | 
				  Rs = DefaultResources<'static, &'static str>,
 | 
			
		||||
 | 
				  As = DefaultArtifacts<'static, &'static str>,
 | 
			
		||||
 | 
				>(Rc<SetupInner<RegularSetupCore<SR, L, B>, LOG, Rs, As>>);
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				// https://github.com/rust-lang/rust/issues/27336
 | 
			
		||||
 | 
				impl<SR, LOG> Setup<SR, LOG> {
 | 
			
		||||
 | 
				  pub fn new(symbol_runner: SR, logger: LOG) -> Self {
 | 
			
		||||
 | 
				    Self::new_with(symbol_runner, logger)
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				impl<L, B, As, SR, LOG, Rs: Hash + Eq> Setup<SR, LOG, L, B, Rs, As> {
 | 
			
		||||
 | 
				  pub fn new_with(symbol_runner: SR, logger: LOG) -> Self {
 | 
			
		||||
 | 
				    Self(Rc::new(SetupInner {
 | 
			
		||||
 | 
				      core: RegularSetupCore::new(symbol_runner),
 | 
			
		||||
 | 
				      logger,
 | 
			
		||||
 | 
				      resources: RefCell::default(),
 | 
			
		||||
 | 
				    }))
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				impl<
 | 
			
		||||
 | 
				    L: 'static,
 | 
			
		||||
 | 
				    B: 'static,
 | 
			
		||||
 | 
				    SR: 'static,
 | 
			
		||||
 | 
				    LOG: 'static + Logger,
 | 
			
		||||
 | 
				    Rs: Hash + Eq + 'static,
 | 
			
		||||
 | 
				    As: 'static,
 | 
			
		||||
 | 
				  > Setup<SR, LOG, L, B, Rs, As>
 | 
			
		||||
 | 
				{
 | 
			
		||||
 | 
				  fn borrow_resources(&self) -> RefMut<'_, Cache<Rs, As>> {
 | 
			
		||||
 | 
				    self.0.resources.borrow_mut()
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  pub(super) async fn add_async<R: AddableResource>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    resource: R,
 | 
			
		||||
 | 
				    force_run: bool,
 | 
			
		||||
 | 
				  ) -> AddResult<R>
 | 
			
		||||
 | 
				  where
 | 
			
		||||
 | 
				    Rs: FromResource<R>,
 | 
			
		||||
 | 
				    As: FromArtifact<R> + Clone,
 | 
			
		||||
 | 
				    R::Artifact: Clone,
 | 
			
		||||
 | 
				    RegularSetupCore<SR, L, B>: SetupCore<R, Self>,
 | 
			
		||||
 | 
				  {
 | 
			
		||||
 | 
				    let (storable_resource, weak_resource) = Rs::from_resource(resource);
 | 
			
		||||
 | 
				    let mut resources = self.borrow_resources();
 | 
			
		||||
 | 
				    if let Some(future) = resources.remove(&storable_resource) {
 | 
			
		||||
 | 
				      assert!(
 | 
			
		||||
 | 
				        !force_run,
 | 
			
		||||
 | 
				        "Forcing to run an already-added resource is a logical error"
 | 
			
		||||
 | 
				      );
 | 
			
		||||
 | 
				      resources.insert(storable_resource, future.clone());
 | 
			
		||||
 | 
				      drop(resources);
 | 
			
		||||
 | 
				      Ok(future.await)
 | 
			
		||||
 | 
				    } else {
 | 
			
		||||
 | 
				      let inner_weak = Rc::downgrade(&self.0);
 | 
			
		||||
 | 
				      let future = Box::pin(async move {
 | 
			
		||||
 | 
				        let this = Self(inner_weak.upgrade().expect("Dangling!"));
 | 
			
		||||
 | 
				        let resource = weak_resource.upgrade().expect("Dangling!");
 | 
			
		||||
 | 
				        // Need to convert Box<Error> to String for Clone for Shared
 | 
			
		||||
 | 
				        this
 | 
			
		||||
 | 
				          .0
 | 
			
		||||
 | 
				          .core
 | 
			
		||||
 | 
				          .add(&this, &this.0.logger, resource, force_run)
 | 
			
		||||
 | 
				          .await
 | 
			
		||||
 | 
				          .map(|(t, did_run)| (As::from_artifact(t), did_run))
 | 
			
		||||
 | 
				          .map_err(|e| e.to_string())
 | 
			
		||||
 | 
				      })
 | 
			
		||||
 | 
				      .shared();
 | 
			
		||||
 | 
				      let future_clone = future.clone();
 | 
			
		||||
 | 
				      resources.insert(
 | 
			
		||||
 | 
				        storable_resource,
 | 
			
		||||
 | 
				        (Box::pin(async move { future_clone.await.unwrap() })
 | 
			
		||||
 | 
				          as Pin<Box<dyn Future<Output = (As, bool)>>>)
 | 
			
		||||
 | 
				          .shared(),
 | 
			
		||||
 | 
				      );
 | 
			
		||||
 | 
				      drop(resources);
 | 
			
		||||
 | 
				      future.await.map_err(|e| e.into())
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				    .map(|(t, did_run)| (t.into_artifact(), did_run))
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  //
 | 
			
		||||
 | 
				  // Legacy
 | 
			
		||||
 | 
				  //
 | 
			
		||||
 | 
				  pub fn add<R: AddableResource>(&self, resource: R) -> AddResult<R>
 | 
			
		||||
 | 
				  where
 | 
			
		||||
 | 
				    RegularSetupCore<SR, L, B>: SetupCore<R, Self>,
 | 
			
		||||
 | 
				    Rs: FromResource<R>,
 | 
			
		||||
 | 
				    As: FromArtifact<R> + Clone,
 | 
			
		||||
 | 
				    R::Artifact: Clone,
 | 
			
		||||
 | 
				  {
 | 
			
		||||
 | 
				    run(self.add_async(resource, false))
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  pub fn add_force<R: AddableResource>(&self, resource: R, force_run: bool) -> AddResult<R>
 | 
			
		||||
 | 
				  where
 | 
			
		||||
 | 
				    RegularSetupCore<SR, L, B>: SetupCore<R, Self>,
 | 
			
		||||
 | 
				    Rs: FromResource<R>,
 | 
			
		||||
 | 
				    As: FromArtifact<R> + Clone,
 | 
			
		||||
 | 
				    R::Artifact: Clone,
 | 
			
		||||
 | 
				  {
 | 
			
		||||
 | 
				    run(self.add_async(resource, force_run))
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  pub fn run_symbol<S: Runnable>(&self, symbol: S, force: bool) -> Result<bool, Box<dyn Error>>
 | 
			
		||||
 | 
				  where
 | 
			
		||||
 | 
				    RegularSetupCore<SR, L, B>: SymbolRunner,
 | 
			
		||||
 | 
				  {
 | 
			
		||||
 | 
				    run(symbol.run(&self.0.core, &self.0.logger, force))
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[cfg(test)]
 | 
			
		||||
 | 
				mod test {
 | 
			
		||||
 | 
				  use super::SymbolRunner;
 | 
			
		||||
 | 
				  use crate::loggers::{Logger, StoringLogger};
 | 
			
		||||
 | 
				  use crate::resources::{FromArtifact, FromResource, Resource};
 | 
			
		||||
 | 
				  use crate::symbols::Symbol;
 | 
			
		||||
 | 
				  use crate::to_artifact::ToArtifact;
 | 
			
		||||
 | 
				  use crate::{ImplementationBuilder, ResourceLocator, Setup};
 | 
			
		||||
 | 
				  use async_trait::async_trait;
 | 
			
		||||
 | 
				  use std::cell::RefCell;
 | 
			
		||||
 | 
				  use std::error::Error;
 | 
			
		||||
 | 
				  use std::fmt::Debug;
 | 
			
		||||
 | 
				  use std::rc::{Rc, Weak};
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  struct TestSymbolRunner {
 | 
			
		||||
 | 
				    count: Rc<RefCell<usize>>,
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[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)
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[derive(Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
 | 
				  struct TestResource<T>(&'static str, T);
 | 
			
		||||
 | 
				  impl<T> Resource for TestResource<T> {
 | 
			
		||||
 | 
				    type Artifact = ();
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[derive(Debug, Hash, PartialEq, Eq)]
 | 
			
		||||
 | 
				  enum Resources {
 | 
			
		||||
 | 
				    A(Rc<TestResource<&'static str>>),
 | 
			
		||||
 | 
				    B(Rc<TestResource<()>>),
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				  impl FromResource<TestResource<&'static str>> for Resources {
 | 
			
		||||
 | 
				    fn from_resource(from: TestResource<&'static str>) -> (Self, Weak<TestResource<&'static str>>) {
 | 
			
		||||
 | 
				      let inner = Rc::new(from);
 | 
			
		||||
 | 
				      (Self::A(Rc::clone(&inner)), Rc::downgrade(&inner))
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				  impl FromResource<TestResource<()>> for Resources {
 | 
			
		||||
 | 
				    fn from_resource(from: TestResource<()>) -> (Self, Weak<TestResource<()>>) {
 | 
			
		||||
 | 
				      let inner = Rc::new(from);
 | 
			
		||||
 | 
				      (Self::B(Rc::clone(&inner)), Rc::downgrade(&inner))
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[derive(Clone)]
 | 
			
		||||
 | 
				  struct Artifacts;
 | 
			
		||||
 | 
				  impl<V> FromArtifact<TestResource<V>> for Artifacts {
 | 
			
		||||
 | 
				    fn from_artifact(from: ()) -> Self {
 | 
			
		||||
 | 
				      Self
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				    fn into_artifact(self) -> () {
 | 
			
		||||
 | 
				      ()
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  struct TestResourceLocator;
 | 
			
		||||
 | 
				  impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
 | 
			
		||||
 | 
				    type Prerequisites = ();
 | 
			
		||||
 | 
				    fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
 | 
			
		||||
 | 
				      ((), ())
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  struct TestImplementationBuilder;
 | 
			
		||||
 | 
				  impl ImplementationBuilder<TestResource<&'static str>> for TestImplementationBuilder {
 | 
			
		||||
 | 
				    type Implementation = TestSymbol;
 | 
			
		||||
 | 
				    type Prerequisites = TestResource<()>;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
 | 
			
		||||
 | 
				      TestResource(resource.1, ())
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				    fn create(
 | 
			
		||||
 | 
				      resource: &TestResource<&'static str>,
 | 
			
		||||
 | 
				      (): &(),
 | 
			
		||||
 | 
				      _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
			
		||||
 | 
				    ) -> Self::Implementation {
 | 
			
		||||
 | 
				      TestSymbol {
 | 
			
		||||
 | 
				        reached: resource.0.chars().next().unwrap().is_uppercase(),
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				  impl ImplementationBuilder<TestResource<()>> for TestImplementationBuilder {
 | 
			
		||||
 | 
				    type Implementation = TestSymbol;
 | 
			
		||||
 | 
				    type Prerequisites = ();
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
 | 
			
		||||
 | 
				    fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Implementation {
 | 
			
		||||
 | 
				      TestSymbol {
 | 
			
		||||
 | 
				        reached: resource.0.chars().next().unwrap().is_uppercase(),
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[derive(Debug)]
 | 
			
		||||
 | 
				  struct TestSymbol {
 | 
			
		||||
 | 
				    reached: bool,
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[async_trait(?Send)]
 | 
			
		||||
 | 
				  impl Symbol for TestSymbol {
 | 
			
		||||
 | 
				    async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
			
		||||
 | 
				      Ok(self.reached)
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				    async fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
			
		||||
 | 
				      Ok(())
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  fn get_setup() -> (
 | 
			
		||||
 | 
				    Rc<RefCell<usize>>,
 | 
			
		||||
 | 
				    Setup<
 | 
			
		||||
 | 
				      TestSymbolRunner,
 | 
			
		||||
 | 
				      StoringLogger,
 | 
			
		||||
 | 
				      TestResourceLocator,
 | 
			
		||||
 | 
				      TestImplementationBuilder,
 | 
			
		||||
 | 
				      Resources,
 | 
			
		||||
 | 
				      Artifacts,
 | 
			
		||||
 | 
				    >,
 | 
			
		||||
 | 
				  ) {
 | 
			
		||||
 | 
				    let count = Rc::new(RefCell::new(0));
 | 
			
		||||
 | 
				    let runner = TestSymbolRunner {
 | 
			
		||||
 | 
				      count: Rc::clone(&count),
 | 
			
		||||
 | 
				    };
 | 
			
		||||
 | 
				    (count, Setup::new_with(runner, StoringLogger::new()))
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[test]
 | 
			
		||||
 | 
				  fn correctly_uses_force() {
 | 
			
		||||
 | 
				    let (count, setup) = get_setup();
 | 
			
		||||
 | 
				    setup.add(TestResource("A", "b")).unwrap();
 | 
			
		||||
 | 
				    assert_eq!(*count.borrow(), 2);
 | 
			
		||||
 | 
				    setup.add(TestResource("A", "b")).unwrap();
 | 
			
		||||
 | 
				    assert_eq!(*count.borrow(), 2);
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    let (count, setup) = get_setup();
 | 
			
		||||
 | 
				    setup.add(TestResource("A", "B")).unwrap();
 | 
			
		||||
 | 
				    assert_eq!(*count.borrow(), 0);
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
@ -0,0 +1,318 @@ | 
			
		|||||
 | 
				use crate::loggers::Logger;
 | 
			
		||||
 | 
				use crate::symbols::Symbol;
 | 
			
		||||
 | 
				use async_trait::async_trait;
 | 
			
		||||
 | 
				use std::error::Error;
 | 
			
		||||
 | 
				use std::fmt;
 | 
			
		||||
 | 
				use std::fmt::Debug;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				pub trait SymbolRunner {
 | 
			
		||||
 | 
				  async fn run_symbol<S: Symbol + Debug, L: Logger>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    symbol: &S,
 | 
			
		||||
 | 
				    logger: &L,
 | 
			
		||||
 | 
				    force: bool,
 | 
			
		||||
 | 
				  ) -> Result<bool, Box<dyn Error>>;
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[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"),
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Clone, Debug, Default)]
 | 
			
		||||
 | 
				pub struct InitializingSymbolRunner;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				impl InitializingSymbolRunner {
 | 
			
		||||
 | 
				  pub fn new() -> Self {
 | 
			
		||||
 | 
				    Self
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  async fn exec_symbol<S: Symbol + Debug, L: Logger>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    symbol: &S,
 | 
			
		||||
 | 
				    logger: &L,
 | 
			
		||||
 | 
				  ) -> Result<(), Box<dyn Error>> {
 | 
			
		||||
 | 
				    logger.info(format!("Executing {:?}", symbol));
 | 
			
		||||
 | 
				    symbol.execute().await?;
 | 
			
		||||
 | 
				    let target_reached = symbol.target_reached().await?;
 | 
			
		||||
 | 
				    logger.trace(format!(
 | 
			
		||||
 | 
				      "Symbol reports target_reached: {:?} (should be true)",
 | 
			
		||||
 | 
				      target_reached
 | 
			
		||||
 | 
				    ));
 | 
			
		||||
 | 
				    if target_reached {
 | 
			
		||||
 | 
				      Ok(())
 | 
			
		||||
 | 
				    } else {
 | 
			
		||||
 | 
				      Err(Box::new(SymbolRunError::ExecuteDidNotReach(())))
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				impl SymbolRunner for InitializingSymbolRunner {
 | 
			
		||||
 | 
				  async fn run_symbol<S: Symbol + Debug, L: Logger>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    symbol: &S,
 | 
			
		||||
 | 
				    logger: &L,
 | 
			
		||||
 | 
				    force: bool,
 | 
			
		||||
 | 
				  ) -> Result<bool, Box<dyn Error>> {
 | 
			
		||||
 | 
				    let executed = if force {
 | 
			
		||||
 | 
				      logger.debug("Forcing symbol execution");
 | 
			
		||||
 | 
				      self.exec_symbol(symbol, logger).await?;
 | 
			
		||||
 | 
				      true
 | 
			
		||||
 | 
				    } else {
 | 
			
		||||
 | 
				      let target_reached = symbol.target_reached().await?;
 | 
			
		||||
 | 
				      if target_reached {
 | 
			
		||||
 | 
				        logger.debug(format!("{:?} already reached", symbol));
 | 
			
		||||
 | 
				      } else {
 | 
			
		||||
 | 
				        logger.trace(format!(
 | 
			
		||||
 | 
				          "Symbol reports target_reached: {:?}",
 | 
			
		||||
 | 
				          target_reached
 | 
			
		||||
 | 
				        ));
 | 
			
		||||
 | 
				        self.exec_symbol(symbol, logger).await?;
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				      !target_reached
 | 
			
		||||
 | 
				    };
 | 
			
		||||
 | 
				    Ok(executed)
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Clone, Debug, Default)]
 | 
			
		||||
 | 
				pub struct DrySymbolRunner;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				impl DrySymbolRunner {
 | 
			
		||||
 | 
				  pub fn new() -> Self {
 | 
			
		||||
 | 
				    Self
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				impl SymbolRunner for DrySymbolRunner {
 | 
			
		||||
 | 
				  async fn run_symbol<S: Symbol + Debug, L: Logger>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    symbol: &S,
 | 
			
		||||
 | 
				    logger: &L,
 | 
			
		||||
 | 
				    force: bool,
 | 
			
		||||
 | 
				  ) -> Result<bool, Box<dyn Error>> {
 | 
			
		||||
 | 
				    let would_execute = if force {
 | 
			
		||||
 | 
				      logger.info(format!("Would force-execute {:?}", symbol));
 | 
			
		||||
 | 
				      true
 | 
			
		||||
 | 
				    } else {
 | 
			
		||||
 | 
				      let target_reached = symbol.target_reached().await?;
 | 
			
		||||
 | 
				      logger.debug(format!(
 | 
			
		||||
 | 
				        "Symbol reports target_reached: {:?}",
 | 
			
		||||
 | 
				        target_reached
 | 
			
		||||
 | 
				      ));
 | 
			
		||||
 | 
				      if !target_reached {
 | 
			
		||||
 | 
				        logger.info(format!("Would execute {:?}", symbol));
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				      !target_reached
 | 
			
		||||
 | 
				    };
 | 
			
		||||
 | 
				    Ok(would_execute)
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[derive(Clone, Debug)]
 | 
			
		||||
 | 
				pub struct ReportingSymbolRunner<R>(R);
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				impl<R> ReportingSymbolRunner<R> {
 | 
			
		||||
 | 
				  pub fn new(symbol_runner: R) -> Self {
 | 
			
		||||
 | 
				    Self(symbol_runner)
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[async_trait(?Send)]
 | 
			
		||||
 | 
				impl<R> SymbolRunner for ReportingSymbolRunner<R>
 | 
			
		||||
 | 
				where
 | 
			
		||||
 | 
				  R: SymbolRunner,
 | 
			
		||||
 | 
				{
 | 
			
		||||
 | 
				  async fn run_symbol<S: Symbol + Debug, L: Logger>(
 | 
			
		||||
 | 
				    &self,
 | 
			
		||||
 | 
				    symbol: &S,
 | 
			
		||||
 | 
				    logger: &L,
 | 
			
		||||
 | 
				    force: bool,
 | 
			
		||||
 | 
				  ) -> Result<bool, Box<dyn Error>> {
 | 
			
		||||
 | 
				    logger.debug(format!("Running symbol {:?}", symbol));
 | 
			
		||||
 | 
				    let res = self.0.run_symbol(symbol, logger, force).await;
 | 
			
		||||
 | 
				    if let Err(ref e) = res {
 | 
			
		||||
 | 
				      logger.info(format!("Failed on {:?} with {}, aborting.", symbol, e))
 | 
			
		||||
 | 
				    } else {
 | 
			
		||||
 | 
				      logger.debug(format!("Successfully finished {:?}", symbol))
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				    res
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				#[cfg(test)]
 | 
			
		||||
 | 
				mod test {
 | 
			
		||||
 | 
				  use super::{DrySymbolRunner, InitializingSymbolRunner, ReportingSymbolRunner, SymbolRunner};
 | 
			
		||||
 | 
				  use crate::async_utils::sleep;
 | 
			
		||||
 | 
				  use crate::async_utils::{run, try_join};
 | 
			
		||||
 | 
				  use crate::loggers::StoringLogger;
 | 
			
		||||
 | 
				  use crate::symbols::Symbol;
 | 
			
		||||
 | 
				  use async_trait::async_trait;
 | 
			
		||||
 | 
				  use std::cell::RefCell;
 | 
			
		||||
 | 
				  use std::error::Error;
 | 
			
		||||
 | 
				  use std::fmt;
 | 
			
		||||
 | 
				  use std::fmt::Debug;
 | 
			
		||||
 | 
				  use std::time::Duration;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[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>,
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[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()),
 | 
			
		||||
 | 
				      }
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  fn run_symbol<S: Symbol + Debug>(s: S) -> Result<bool, Box<dyn Error>> {
 | 
			
		||||
 | 
				    run(InitializingSymbolRunner::new().run_symbol(&s, &StoringLogger::new(), false))
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[test]
 | 
			
		||||
 | 
				  fn nothing_needed_to_be_done() {
 | 
			
		||||
 | 
				    let result = run_symbol(DummySymbol::new(vec![Ok(true)], vec![Ok(())]));
 | 
			
		||||
 | 
				    assert!(result.is_ok());
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[test]
 | 
			
		||||
 | 
				  fn everything_is_ok() {
 | 
			
		||||
 | 
				    let result = run_symbol(DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]));
 | 
			
		||||
 | 
				    assert!(result.is_ok());
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[test]
 | 
			
		||||
 | 
				  fn executing_did_not_change_state() {
 | 
			
		||||
 | 
				    let result = run_symbol(DummySymbol::new(vec![Ok(false), Ok(false)], vec![Ok(())]));
 | 
			
		||||
 | 
				    assert_eq!(
 | 
			
		||||
 | 
				      result.unwrap_err().to_string(),
 | 
			
		||||
 | 
				      "Target not reached after executing symbol"
 | 
			
		||||
 | 
				    );
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[test]
 | 
			
		||||
 | 
				  fn executing_did_not_work() {
 | 
			
		||||
 | 
				    let result = run_symbol(DummySymbol::new(
 | 
			
		||||
 | 
				      vec![Ok(false)],
 | 
			
		||||
 | 
				      vec![Err(Box::new(DummySymbolError::Error(())) as Box<dyn Error>)],
 | 
			
		||||
 | 
				    ));
 | 
			
		||||
 | 
				    assert_eq!(result.unwrap_err().to_string(), "Dummy symbol error");
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[derive(Debug)]
 | 
			
		||||
 | 
				  struct SleeperSymbol;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[async_trait(?Send)]
 | 
			
		||||
 | 
				  impl Symbol for SleeperSymbol {
 | 
			
		||||
 | 
				    async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
			
		||||
 | 
				      sleep(Duration::from_millis(0)).await;
 | 
			
		||||
 | 
				      Ok(true)
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				    async fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
			
		||||
 | 
				      unimplemented!();
 | 
			
		||||
 | 
				    }
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  #[test]
 | 
			
		||||
 | 
				  fn actually_support_parallel_execution() {
 | 
			
		||||
 | 
				    run(async {
 | 
			
		||||
 | 
				      let s1 = SleeperSymbol;
 | 
			
		||||
 | 
				      let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      let l1 = StoringLogger::new();
 | 
			
		||||
 | 
				      let l2 = StoringLogger::new();
 | 
			
		||||
 | 
				      let runner1 = InitializingSymbolRunner::new();
 | 
			
		||||
 | 
				      let result = try_join!(
 | 
			
		||||
 | 
				        runner1.run_symbol(&s1, &l1, false),
 | 
			
		||||
 | 
				        runner1.run_symbol(&s2, &l2, false),
 | 
			
		||||
 | 
				      )
 | 
			
		||||
 | 
				      .unwrap();
 | 
			
		||||
 | 
				      assert_eq!(result, (false, true));
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
 | 
			
		||||
 | 
				      let l1 = StoringLogger::new();
 | 
			
		||||
 | 
				      let l2 = StoringLogger::new();
 | 
			
		||||
 | 
				      let runner2 = DrySymbolRunner::new();
 | 
			
		||||
 | 
				      let result = try_join!(
 | 
			
		||||
 | 
				        runner2.run_symbol(&s1, &l1, false),
 | 
			
		||||
 | 
				        runner2.run_symbol(&s2, &l2, false),
 | 
			
		||||
 | 
				      )
 | 
			
		||||
 | 
				      .unwrap();
 | 
			
		||||
 | 
				      assert_eq!(result, (false, true));
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
 | 
			
		||||
 | 
				      let l1 = StoringLogger::new();
 | 
			
		||||
 | 
				      let l2 = StoringLogger::new();
 | 
			
		||||
 | 
				      let runner3 = ReportingSymbolRunner::new(runner1);
 | 
			
		||||
 | 
				      let result = try_join!(
 | 
			
		||||
 | 
				        runner3.run_symbol(&s1, &l1, false),
 | 
			
		||||
 | 
				        runner3.run_symbol(&s2, &l2, false),
 | 
			
		||||
 | 
				      )
 | 
			
		||||
 | 
				      .unwrap();
 | 
			
		||||
 | 
				      assert_eq!(result, (false, true));
 | 
			
		||||
 | 
				    });
 | 
			
		||||
 | 
				  }
 | 
			
		||||
 | 
				}
 | 
			
		||||
@ -0,0 +1,9 @@ | 
			
		|||||
 | 
				use crate::resources::Resource;
 | 
			
		||||
 | 
				use crate::to_artifact::ToArtifact;
 | 
			
		||||
 | 
				use std::error::Error;
 | 
			
		||||
 | 
				use std::fmt::Debug;
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				pub trait AddableResource: 'static + Resource + Debug {}
 | 
			
		||||
 | 
				impl<R> AddableResource for R where R: 'static + Resource + Debug {}
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				pub type AddResult<R> = Result<(<R as ToArtifact>::Artifact, bool), Box<dyn Error>>;
 | 
			
		||||
@ -1,4 +1,4 @@ | 
			
		|||||
mod checkout;
 | 
				mod checkout;
 | 
			
		||||
//pub mod submodules;
 | 
				 | 
			
		||||
 | 
				pub mod submodules;
 | 
			
		||||
 | 
				
 | 
			
		||||
pub use checkout::Checkout;
 | 
				pub use checkout::Checkout;
 | 
			
		||||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue