summaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 8c5708f6792ac419c7021da0ba30f9cd77e8bdc2 (plain)
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
use im::{HashMap, HashSet};
use std::{fmt, path::Path, rc::Rc};

mod recipe;

#[derive(Debug)]
enum Error {
	TaskNotFound(String),
	DependencyCycle(String),
}

impl fmt::Display for Error {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match self {
			Error::TaskNotFound(id) => write!(f, "Task not found: {}", id),
			Error::DependencyCycle(id) => write!(f, "DependencyCycle: {}", id),
		}
	}
}

impl std::error::Error for Error {}

type Result<T> = std::result::Result<T, Error>;

#[derive(Default)]
struct Context {
	tasks: HashMap<String, Rc<recipe::Task>>,
}

impl Context {
	fn collect_subtasks<'a>(
		&'a self,
		queued: &HashSet<&'a str>,
		id: &str,
	) -> Result<HashSet<&'a str>> {
		let (task_id, task) = match self.tasks.get_key_value(id) {
			Some(t) => t,
			None => {
				return Err(Error::TaskNotFound(id.to_string()));
			}
		};

		if queued.contains(id) {
			return Err(Error::DependencyCycle(id.to_string()));
		}

		let queued_sub = queued.update(task_id);

		let mut subtasks: HashSet<&'a str> = HashSet::new();
		subtasks.insert(task_id);

		for dep in &task.depends {
			let deptasks = self.collect_subtasks(&queued_sub, dep)?;
			subtasks = subtasks.union(deptasks);
		}

		Ok(subtasks)
	}

	fn collect_tasks(&self, id: &str) -> Result<HashSet<&str>> {
		self.collect_subtasks(&HashSet::new(), id)
	}
}

fn main() -> Result<()> {
	let recipes = recipe::read_recipes(Path::new("examples")).unwrap();

	let mut ctx = Context::default();
	for (recipe_name, recipe) in recipes {
		for (task_name, task) in recipe.tasks {
			let full_name = format!("{}:{}", recipe_name, task_name);
			ctx.tasks.insert(full_name, Rc::new(task));
		}
	}

	let queue = ctx.collect_tasks("ls:build")?;
	let (runnable, queued): (HashSet<&str>, HashSet<&str>) = queue
		.into_iter()
		.partition(|id| ctx.tasks.get(*id).unwrap().depends.is_empty());

	for t in &runnable {
		println!("Runnable: {} ({:?})", t, ctx.tasks.get(*t).unwrap().run);
	}
	for t in &queued {
		println!("Queued: {} ({:?})", t, ctx.tasks.get(*t).unwrap().run);
	}

	Ok(())
}