use std::collections::{HashMap, HashSet}; use std::fmt; use crate::types::*; #[derive(Debug)] pub struct DepChain(pub Vec); impl fmt::Display for DepChain { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut first = true; for task in self.0.iter().rev() { if !first { write!(f, " -> ")?; } write!(f, "{}", task)?; first = false; } Ok(()) } } #[derive(Debug)] pub enum Error { TaskNotFound(DepChain), DependencyCycle(DepChain), } impl Error { fn extend(&mut self, task: TaskRef) { let tasks = match self { Error::TaskNotFound(ref mut tasks) => tasks, Error::DependencyCycle(ref mut tasks) => tasks, }; tasks.0.push(task); } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::TaskNotFound(tasks) => { write!(f, "Task not found: {}", tasks) } Error::DependencyCycle(tasks) => { write!(f, "Dependency Cycle: {}", tasks) } } } } impl std::error::Error for Error {} #[derive(Debug, PartialEq)] enum ResolveState { Resolving, Resolved, } #[derive(Debug)] pub struct Resolver<'a> { tasks: &'a TaskMap, resolve_state: HashMap, } impl<'a> Resolver<'a> { pub fn new(tasks: &'a TaskMap) -> Self { Resolver { tasks, resolve_state: HashMap::new(), } } fn tasks_resolved(&self) -> bool { self.resolve_state .values() .all(|resolved| *resolved == ResolveState::Resolved) } pub fn add_task(&mut self, task: &TaskRef) -> Vec { match self.resolve_state.get(task) { Some(ResolveState::Resolving) => { return vec![Error::DependencyCycle(DepChain(vec![task.clone()]))] } Some(ResolveState::Resolved) => return vec![], None => (), } let task_def = match self.tasks.get(task) { None => return vec![Error::TaskNotFound(DepChain(vec![task.clone()]))], Some(task_def) => task_def, }; self.resolve_state .insert(task.clone(), ResolveState::Resolving); let mut ret = Vec::new(); for dep in &task_def.depends { let errors = self.add_task(dep); for mut error in errors { error.extend(task.clone()); ret.push(error); } } if ret.is_empty() { *self .resolve_state .get_mut(task) .expect("Missing resolve_state") = ResolveState::Resolved; } else { self.resolve_state.remove(task); } ret } pub fn add_goal(&mut self, task: &TaskRef) -> Vec { let ret = self.add_task(task); debug_assert!(self.tasks_resolved()); ret } pub fn to_taskset(self) -> HashSet { debug_assert!(self.tasks_resolved()); self.resolve_state .into_iter() .map(|entry| entry.0) .collect() } }