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

196 lines
4.7 KiB

use std::error::Error;
use std::fmt;
use resources::Resource;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
pub struct Hook<A, B>
where
A: Symbol,
B: Symbol,
{
a: A,
b: B,
}
// A and B are executed if either A or B are not reached
impl<A, B> Hook<A, B>
where
A: Symbol,
B: Symbol,
{
pub fn new(a: A, b: B) -> Self {
Hook { a, b }
}
}
impl<A, B> Symbol for Hook<A, B>
where
A: Symbol,
B: Symbol,
{
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
self.a.target_reached().and_then(|reached| {
if reached {
self.b.target_reached()
} else {
Ok(reached)
}
})
}
fn execute(&self) -> Result<(), Box<dyn Error>> {
self.a.execute()?;
self.b.execute()
}
fn get_prerequisites(&self) -> Vec<Resource> {
let mut r = vec![];
r.extend(self.a.get_prerequisites().into_iter());
r.extend(self.b.get_prerequisites().into_iter());
r
}
fn provides(&self) -> Option<Vec<Resource>> {
let mut r = vec![];
if let Some(provides) = self.a.provides() {
r.extend(provides.into_iter());
}
if let Some(provides) = self.b.provides() {
r.extend(provides.into_iter());
}
if r.is_empty() {
None
} else {
Some(r)
}
}
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
where
Self: 'a,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
impl<A, B> fmt::Display for Hook<A, B>
where
A: Symbol,
B: Symbol,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "Hook {} and then {}", self.a, self.b)
}
}
#[cfg(test)]
mod test {
use std::error::Error;
use std::fmt;
use symbols::hook::Hook;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
struct ErrSymbol(String);
impl Symbol for ErrSymbol {
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
Err(self.0.clone().into())
}
fn execute(&self) -> Result<(), Box<dyn Error>> {
Err(self.0.clone().into())
}
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
where
Self: 'a,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
impl fmt::Display for ErrSymbol {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "")
}
}
struct OkSymbol(bool);
impl Symbol for OkSymbol {
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
Ok(self.0)
}
fn execute(&self) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
where
Self: 'a,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
impl fmt::Display for OkSymbol {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "")
}
}
#[test]
fn first_target_reached_fails() {
let res = Hook::new(ErrSymbol("first".into()), ErrSymbol("second".into())).target_reached();
assert_eq!(res.unwrap_err().description(), "first");
}
#[test]
fn first_target_not_reached() {
let res = Hook::new(OkSymbol(false), ErrSymbol("second".into())).target_reached();
assert_eq!(res.unwrap(), false);
}
#[test]
fn second_target_reached_fails() {
let res = Hook::new(OkSymbol(true), ErrSymbol("second".into())).target_reached();
assert_eq!(res.unwrap_err().description(), "second");
}
#[test]
fn second_target_not_reached() {
let res = Hook::new(OkSymbol(true), OkSymbol(false)).target_reached();
assert_eq!(res.unwrap(), false);
}
#[test]
fn everything_reached() {
let res = Hook::new(OkSymbol(true), OkSymbol(true)).target_reached();
assert_eq!(res.unwrap(), true);
}
#[test]
fn first_execute_fails() {
let res = Hook::new(ErrSymbol("first".into()), ErrSymbol("second".into())).execute();
assert_eq!(res.unwrap_err().description(), "first");
}
#[test]
fn second_execute_fails() {
let res = Hook::new(OkSymbol(true), ErrSymbol("second".into())).execute();
assert_eq!(res.unwrap_err().description(), "second");
}
#[test]
fn everything_executes() {
let res = Hook::new(OkSymbol(true), OkSymbol(true)).execute();
assert_eq!(res.unwrap(), ());
}
}