targets 0.1.1

Some helpers to get you started with declarative programming in Rust
Documentation
use std::any::Any;
use std::collections::HashMap;
use std::fmt;

pub struct Target<'a> {
    pub key: String,
    pub dependencies: Vec<&'a Target<'a>>,
    pub build_order: Vec<&'a Target<'a>>,
    pub recipe: fn(Vec<&Box<Any>>) -> Box<Any>,
}

impl<'a> fmt::Debug for Target<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let deps = self.dependencies.iter().map(|d| d.key.clone()).collect::<Vec<String>>().join(", ");
        let build_order = self.build_order.iter().map(|d| d.key.clone()).collect::<Vec<String>>().join(", ");
        write!(f, "Target {{ key: {}, dependencies: {}, build_order: {} }}", self.key, deps, build_order)
    }
}

impl<'a> Target<'a> {
    fn build(&self, deps: &HashMap<String, Box<Any>>) -> Box<Any> {
        let args: Vec<&Box<Any>> = self.dependencies.iter()
            .map(|d| deps.get(&d.key).unwrap())
            .collect();

        (self.recipe)(args)
    }

    pub fn build_order(deps: Vec<&'a Target<'a>>) -> Vec<&'a Target<'a>> {
        let mut all_deps : Vec<&Target> = Vec::new();
        deps.iter().for_each(|dep| all_deps.extend(dep.build_order.clone()));
        all_deps.extend(deps);

        let mut i: usize = 0;
        while i != all_deps.len() {
            if all_deps[..i].contains(&all_deps[i]) {
                all_deps.remove(i);
            } else {
                i += 1;
            }
        }

        all_deps
    }
}

impl<'a> PartialEq for Target<'a> {
    fn eq(&self, other: &Target) -> bool {
        self.key == other.key
    }
}

pub struct Builder {
    resolved: HashMap<String,Box<Any>>,
}

impl fmt::Debug for Builder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Builder {{ resolved: {} }}", self.resolved.keys().map(|d| d.clone()).collect::<Vec<String>>().join(", "))
    }
}

impl Builder {
    pub fn new(state: Box<Any>) -> Self {
        // all list of targets we have built so far
        let mut resolved : HashMap<String,Box<Any>> = HashMap::new();
        resolved.insert(String::from("_state"), state);

        Builder {
            resolved,
        }
    }

    pub fn build(&mut self, buildable: &Target) -> &Box<Any> {
        // do we have it already?
        if self.resolved.contains_key(&buildable.key) {
            // awkward if/return because of mutable borrows
            return self.resolved.get(&buildable.key).unwrap();
        }

        // all the targets we need to build still
        let mut deps: Vec<&&Target> = buildable.build_order.iter()
            .filter(|dep| !self.resolved.contains_key(&dep.key))
            .collect();

        // iterate in rounds, building targets that have their dependencies fulfilled
        while deps.len() != 0 {
            let target = deps.remove(0);
            let built = target.build(&self.resolved);
            assert_eq!(self.resolved.insert(target.key.clone(), built).is_none(), true);
        }

        let built = buildable.build(&self.resolved);
        assert_eq!(self.resolved.insert(buildable.key.clone(), built).is_none(), true);

        self.resolved.get(&buildable.key).unwrap()
    }
}

#[macro_export]
macro_rules! capture {
    ($name:ident) => {
        let $name = Target {
            key: String::from("_state"),
            dependencies: vec![],
            build_order: vec![],
            recipe: |_deps| Box::new(1),
        };
    };
}

#[macro_export]
macro_rules! target {
    (fn $name:ident ($($arg:ident : $argtype:ty),+) -> $ret:ty = $body:expr) => {
        let $name = Target {
            key: String::from(stringify!($name)),
            dependencies: vec![$(&$arg),*],
            build_order: Target::build_order(vec![$(&$arg),*]),
            recipe: |mut deps| {
                $(
                    let $arg = deps.remove(0).downcast_ref::<$argtype>().expect(stringify!($arg));
                )*
                Box::new($body)
            },
        };
    };
}

#[macro_export]
macro_rules! build {
    ($target:ident : $type:ty, $builder:ident) => {
        {
            let built = $builder.build(&$target);
            built.downcast_ref::<$type>().unwrap()
        }
    }
}