targets/
lib.rs

1use std::any::Any;
2use std::collections::HashMap;
3use std::fmt;
4
5pub struct Target<'a> {
6    pub key: String,
7    pub dependencies: Vec<&'a Target<'a>>,
8    pub build_order: Vec<&'a Target<'a>>,
9    pub recipe: fn(Vec<&Box<Any>>) -> Box<Any>,
10}
11
12impl<'a> fmt::Debug for Target<'a> {
13    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
14        let deps = self.dependencies.iter().map(|d| d.key.clone()).collect::<Vec<String>>().join(", ");
15        let build_order = self.build_order.iter().map(|d| d.key.clone()).collect::<Vec<String>>().join(", ");
16        write!(f, "Target {{ key: {}, dependencies: {}, build_order: {} }}", self.key, deps, build_order)
17    }
18}
19
20impl<'a> Target<'a> {
21    fn build(&self, deps: &HashMap<String, Box<Any>>) -> Box<Any> {
22        let args: Vec<&Box<Any>> = self.dependencies.iter()
23            .map(|d| deps.get(&d.key).unwrap())
24            .collect();
25
26        (self.recipe)(args)
27    }
28
29    pub fn build_order(deps: Vec<&'a Target<'a>>) -> Vec<&'a Target<'a>> {
30        let mut all_deps : Vec<&Target> = Vec::new();
31        deps.iter().for_each(|dep| all_deps.extend(dep.build_order.clone()));
32        all_deps.extend(deps);
33
34        let mut i: usize = 0;
35        while i != all_deps.len() {
36            if all_deps[..i].contains(&all_deps[i]) {
37                all_deps.remove(i);
38            } else {
39                i += 1;
40            }
41        }
42
43        all_deps
44    }
45}
46
47impl<'a> PartialEq for Target<'a> {
48    fn eq(&self, other: &Target) -> bool {
49        self.key == other.key
50    }
51}
52
53pub struct Builder {
54    resolved: HashMap<String,Box<Any>>,
55}
56
57impl fmt::Debug for Builder {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        write!(f, "Builder {{ resolved: {} }}", self.resolved.keys().map(|d| d.clone()).collect::<Vec<String>>().join(", "))
60    }
61}
62
63impl Builder {
64    pub fn new(state: Box<Any>) -> Self {
65        // all list of targets we have built so far
66        let mut resolved : HashMap<String,Box<Any>> = HashMap::new();
67        resolved.insert(String::from("_state"), state);
68
69        Builder {
70            resolved,
71        }
72    }
73
74    pub fn build(&mut self, buildable: &Target) -> &Box<Any> {
75        // do we have it already?
76        if self.resolved.contains_key(&buildable.key) {
77            // awkward if/return because of mutable borrows
78            return self.resolved.get(&buildable.key).unwrap();
79        }
80
81        // all the targets we need to build still
82        let mut deps: Vec<&&Target> = buildable.build_order.iter()
83            .filter(|dep| !self.resolved.contains_key(&dep.key))
84            .collect();
85
86        // iterate in rounds, building targets that have their dependencies fulfilled
87        while deps.len() != 0 {
88            let target = deps.remove(0);
89            let built = target.build(&self.resolved);
90            assert_eq!(self.resolved.insert(target.key.clone(), built).is_none(), true);
91        }
92
93        let built = buildable.build(&self.resolved);
94        assert_eq!(self.resolved.insert(buildable.key.clone(), built).is_none(), true);
95
96        self.resolved.get(&buildable.key).unwrap()
97    }
98}
99
100#[macro_export]
101macro_rules! capture {
102    ($name:ident) => {
103        let $name = Target {
104            key: String::from("_state"),
105            dependencies: vec![],
106            build_order: vec![],
107            recipe: |_deps| Box::new(1),
108        };
109    };
110}
111
112#[macro_export]
113macro_rules! target {
114    (fn $name:ident ($($arg:ident : $argtype:ty),+) -> $ret:ty = $body:expr) => {
115        let $name = Target {
116            key: String::from(stringify!($name)),
117            dependencies: vec![$(&$arg),*],
118            build_order: Target::build_order(vec![$(&$arg),*]),
119            recipe: |mut deps| {
120                $(
121                    let $arg = deps.remove(0).downcast_ref::<$argtype>().expect(stringify!($arg));
122                )*
123                Box::new($body)
124            },
125        };
126    };
127}
128
129#[macro_export]
130macro_rules! build {
131    ($target:ident : $type:ty, $builder:ident) => {
132        {
133            let built = $builder.build(&$target);
134            built.downcast_ref::<$type>().unwrap()
135        }
136    }
137}