use crate::unit::{UnitHandle, Unit}; use std::collections::{HashMap, VecDeque, HashSet}; use crate::config::loader::Loader; #[derive(Debug, Clone, Default)] pub struct DependencyTree { known: HashSet, dependents: HashMap>, dependencies: HashMap>, } impl DependencyTree { pub fn add_owner(&mut self, owner: String) { self.known.insert(owner); } pub fn add_dependency(&mut self, owner: String, dependency: String) { if owner == dependency { return; } self.dependencies .entry(owner.clone()) .or_default() .insert(dependency.clone()); self.dependents .entry(dependency.clone()) .or_default() .insert(owner.clone()); self.known.insert(dependency); self.known.insert(owner); } /// Gets the bottom of the dependency tree /// These are the items -without- dependencies pub fn get_bottom(&self) -> Vec { self .known .iter() .filter(|item| !self.dependencies.get(*item).map(|l| l.len() == 0).unwrap_or(true)) .map(|item| item.clone()) .collect() } /// Gets the top of the dependency tree /// These are the items -without- dependents pub fn get_top(&self) -> Vec { self .known .iter() .filter(|item| self.dependents.get(*item).map(|l| l.len() == 0).unwrap_or(true)) .map(|item| item.clone()) .collect() } pub fn get_dependencies(&self, item: &str) -> Vec { self .dependencies .get(item) .map(|set| set.iter().map(|x| x.clone()).collect::>()) .unwrap_or(Vec::new()) } pub fn get_depedents(&self, item: &str) -> Vec { self .dependents .get(item) .map(|set| set.iter().map(|x| x.clone()).collect::>()) .unwrap_or(Vec::new()) } } #[derive(Debug, Clone, Default)] pub struct Registry { loader: Loader, units: HashMap, } impl Registry { pub fn with_loader(loader: Loader) -> Registry { Registry { loader, units: Default::default(), } } pub async fn get_dependency_tree(&mut self, unit_name: &str) -> DependencyTree { let mut queue = VecDeque::new(); let mut done = HashSet::new(); let mut dependencies: DependencyTree = Default::default(); queue.push_back(unit_name.to_string()); while let Some(item) = queue.pop_front() { let unit = if let Some(unit) = self.load_unit(&item).await { unit } else { done.insert(item); continue; }; for dep in &unit.wants { if done.contains(dep) { continue; } queue.push_back(dep.clone()); } for dep in &unit.requires { if done.contains(dep) { continue; } queue.push_back(dep.clone()); } for before in &unit.before { dependencies.add_dependency(before.clone(), unit.name.clone()); } for after in &unit.after { dependencies.add_dependency(unit.name.clone(), after.clone()); } } dependencies } pub async fn load_unit(&mut self, unit_name: &str) -> Option<&Unit> { let config = self .units .entry(unit_name.to_string()) .or_insert_with(|| UnitHandle::unloaded(unit_name)); if !config.is_loaded() { if let Some(unit) = self.loader.load_unit(unit_name).await { config.update(unit); } } config.get_config() } }