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: HashSet, 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: HashSet::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(dep)) } fn run_one(&mut self, runner: &impl runner::Runner) -> runner::Result<()> { let task = self.tasks_runnable.pop().expect("No runnable tasks left"); runner.run(self.tasks, &task)?; let rdeps = self.rdeps.get(&task); self.tasks_done.insert(task); 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"); Ok(()) } }