use std::collections::{HashMap, HashSet}; use crate::{runner, types::*}; #[derive(Debug)] pub struct Executor<'a> { tasks: &'a TaskMap, tasks_blocked: HashSet, tasks_runnable: Vec, tasks_done: HashMap, rdeps: HashMap>, } impl<'a> Executor<'a> { pub fn new(tasks: &'a TaskMap, taskset: HashSet) -> Self { let mut exc = Executor { tasks, tasks_blocked: HashSet::new(), tasks_runnable: Vec::new(), tasks_done: HashMap::new(), rdeps: HashMap::new(), }; for task in taskset { let task_def = tasks.get(&task).expect("Invalid TaskRef"); if task_def.depends.is_empty() { exc.tasks_runnable.push(task); } else { for dep in &task_def.depends { let rdep = exc.rdeps.entry(dep.clone()).or_default(); rdep.push(task.clone()); } exc.tasks_blocked.insert(task); } } exc } fn deps_satisfied(&self, task: &TaskRef) -> bool { let task_def = self.tasks.get(task).expect("Invalid TaskRef"); task_def .depends .iter() .all(|dep| self.tasks_done.contains_key(dep)) } fn task_deps(&self, task: &TaskRef) -> HashMap { let task_def = self.tasks.get(&task).expect("Invalid TaskRef"); task_def .depends .iter() .map(|dep| { ( dep.clone(), self.tasks_done .get(dep) .expect("Invalid dependency") .output_hash, ) }) .collect() } fn run_one(&mut self, runner: &impl runner::Runner) -> runner::Result<()> { let task = self.tasks_runnable.pop().expect("No runnable tasks left"); let task_deps = self.task_deps(&task); let hash = runner.run(self.tasks, &task)?; let output = TaskOutput { task_ref: task.clone(), depends: task_deps, output_hash: hash, }; let rdeps = self.rdeps.get(&task); self.tasks_done.insert(task, output); for rdep in rdeps.unwrap_or(&Vec::new()) { if !self.tasks_blocked.contains(rdep) { continue; } if self.deps_satisfied(rdep) { self.tasks_blocked.remove(rdep); self.tasks_runnable.push(rdep.clone()); } } Ok(()) } pub fn run(&mut self, runner: &impl runner::Runner) -> runner::Result<()> { while !self.tasks_runnable.is_empty() { self.run_one(runner)?; } assert!(self.tasks_blocked.is_empty(), "No runnable tasks left"); println!("{}", serde_json::to_string_pretty(&self.tasks_done)?); Ok(()) } }