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 {
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> {
if self.resolved.contains_key(&buildable.key) {
return self.resolved.get(&buildable.key).unwrap();
}
let mut deps: Vec<&&Target> = buildable.build_order.iter()
.filter(|dep| !self.resolved.contains_key(&dep.key))
.collect();
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()
}
}
}