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.

218 lines
5.4 KiB

use std::error::Error;
use std::fmt;
use resources::Resource;
use symbols::{Action, Symbol, SymbolRunner};
pub struct List<'a> {
symbols: Vec<Box<dyn Symbol + 'a>>,
}
impl<'a> List<'a> {
pub fn new(symbols: Vec<Box<dyn Symbol + 'a>>) -> Self {
List { symbols }
}
}
impl<'a> Symbol for List<'a> {
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
for symbol in &self.symbols {
if !try!(symbol.target_reached()) {
return Ok(false);
}
}
Ok(true)
}
fn execute(&self) -> Result<(), Box<dyn Error>> {
for symbol in &self.symbols {
try!(symbol.execute());
}
Ok(())
}
fn get_prerequisites(&self) -> Vec<Resource> {
let mut r = vec![];
for symbol in &self.symbols {
for req in symbol.get_prerequisites() {
if self.provides().map_or(true, |p| !p.contains(&req)) {
r.push(req)
}
}
}
r
}
fn provides(&self) -> Option<Vec<Resource>> {
let mut r = vec![];
for symbol in &self.symbols {
if let Some(provides) = symbol.provides() {
r.extend(provides.into_iter());
}
}
if r.is_empty() {
None
} else {
Some(r)
}
}
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
Box::new(SymbolListAction::new(runner, &self.symbols))
}
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
where
Self: 'b,
{
Box::new(ListAction::new(
self
.symbols
.into_iter()
.map(|s| s.into_action(runner))
.collect(),
))
}
}
impl<'a, A: 'a + Symbol, B: 'a + Symbol> From<(A, B)> for List<'a> {
fn from((a, b): (A, B)) -> Self {
Self::new(vec![Box::new(a), Box::new(b)])
}
}
impl<'a, A: 'a + Symbol, B: 'a + Symbol, C: 'a + Symbol> From<(A, B, C)> for List<'a> {
fn from((a, b, c): (A, B, C)) -> Self {
Self::new(vec![Box::new(a), Box::new(b), Box::new(c)])
}
}
impl<'a, A: 'a + Symbol, B: 'a + Symbol, C: 'a + Symbol, D: 'a + Symbol> From<(A, B, C, D)>
for List<'a>
{
fn from((a, b, c, d): (A, B, C, D)) -> Self {
Self::new(vec![Box::new(a), Box::new(b), Box::new(c), Box::new(d)])
}
}
struct SymbolListAction<'a> {
runner: &'a dyn SymbolRunner,
symbols: &'a [Box<dyn Symbol + 'a>],
}
impl<'a> SymbolListAction<'a> {
fn new(runner: &'a dyn SymbolRunner, symbols: &'a [Box<dyn Symbol + 'a>]) -> Self {
Self { runner, symbols }
}
}
impl<'a> Action for SymbolListAction<'a> {
fn run(&self) -> Result<(), Box<dyn Error>> {
for symbol in self.symbols {
try!(symbol.as_action(self.runner).run());
}
Ok(())
}
}
pub struct ListAction<'a> {
actions: Vec<Box<dyn Action + 'a>>,
}
impl<'a> ListAction<'a> {
pub fn new(actions: Vec<Box<dyn Action + 'a>>) -> Self {
Self { actions }
}
}
impl<'a> Action for ListAction<'a> {
fn run(&self) -> Result<(), Box<dyn Error>> {
for action in &self.actions {
try!(action.run());
}
Ok(())
}
}
impl<'a> fmt::Display for List<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
try!(write!(f, "List [ "));
for symbol in &self.symbols {
try!(write!(f, "{} ", symbol));
}
write!(f, "]")
}
}
/*
#[cfg(test)]
mod test {
use std::error::Error;
use std::fmt;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
use symbols::hook::List;
struct ErrSymbol(String);
impl Symbol for ErrSymbol {
fn target_reached(&self) -> Result<bool, Box<Error>> { Err(self.0.clone().into()) }
fn execute(&self) -> Result<(), Box<Error>> { Err(self.0.clone().into()) }
}
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<Error>> { Ok(self.0) }
fn execute(&self) -> Result<(), Box<Error>> { Ok(()) }
}
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 = List::new(ErrSymbol("first".into()), ErrSymbol("second".into())).target_reached();
assert_eq!(res.unwrap_err().description(), "first");
}
#[test]
fn first_target_not_reached() {
let res = List::new(OkSymbol(false), ErrSymbol("second".into())).target_reached();
assert_eq!(res.unwrap(), false);
}
#[test]
fn second_target_reached_fails() {
let res = List::new(OkSymbol(true), ErrSymbol("second".into())).target_reached();
assert_eq!(res.unwrap_err().description(), "second");
}
#[test]
fn second_target_not_reached() {
let res = List::new(OkSymbol(true), OkSymbol(false)).target_reached();
assert_eq!(res.unwrap(), false);
}
#[test]
fn everything_reached() {
let res = List::new(OkSymbol(true), OkSymbol(true)).target_reached();
assert_eq!(res.unwrap(), true);
}
#[test]
fn first_execute_fails() {
let res = List::new(ErrSymbol("first".into()), ErrSymbol("second".into())).execute();
assert_eq!(res.unwrap_err().description(), "first");
}
#[test]
fn second_execute_fails() {
let res = List::new(OkSymbol(true), ErrSymbol("second".into())).execute();
assert_eq!(res.unwrap_err().description(), "second");
}
#[test]
fn everything_executes() {
let res = List::new(OkSymbol(true), OkSymbol(true)).execute();
assert_eq!(res.unwrap(), ());
}
}
*/