1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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()
        }
    }
}