use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
use crate::req::{PrereqGroup, Requirement};
pub struct ReqTree {
reqs: HashMap<String, Requirement>,
dependents: HashMap<String, HashSet<String>>,
}
impl ReqTree {
#[must_use]
pub fn new() -> Self {
Self {
reqs: HashMap::new(),
dependents: HashMap::new(),
}
}
pub fn insert(&mut self, req: Requirement) {
let name = req.name_or_default();
for prereq in req.prereqs.iter().flat_map(PrereqGroup::alternatives) {
self.dependents
.entry(prereq.clone())
.or_default()
.insert(name.clone());
}
self.reqs.insert(name, req);
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&Requirement> {
self.reqs.get(name)
}
#[must_use]
pub fn prereqs(&self, name: &str) -> Option<&BTreeSet<PrereqGroup>> {
self.reqs.get(name).map(|r| &r.prereqs)
}
#[must_use]
pub fn dependents(&self, name: &str) -> Option<&HashSet<String>> {
self.dependents.get(name)
}
#[must_use]
pub fn all_prereqs(&self, name: &str) -> HashSet<String> {
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
if let Some(req) = self.reqs.get(name) {
queue.extend(req.prereqs.iter().flat_map(|g| g.alternatives().cloned()));
}
while let Some(current) = queue.pop_front() {
if visited.insert(current.clone())
&& let Some(req) = self.reqs.get(¤t)
{
queue.extend(req.prereqs.iter().flat_map(|g| g.alternatives().cloned()));
}
}
visited
}
#[must_use]
pub fn all_dependents(&self, name: &str) -> HashSet<String> {
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
if let Some(deps) = self.dependents.get(name) {
queue.extend(deps.iter().cloned());
}
while let Some(current) = queue.pop_front() {
if visited.insert(current.clone())
&& let Some(deps) = self.dependents.get(¤t)
{
queue.extend(deps.iter().cloned());
}
}
visited
}
#[must_use]
pub fn find_cycle(&self) -> Option<Vec<String>> {
let mut visited = HashSet::new();
let mut stack = HashSet::new();
let mut path = Vec::new();
for name in self.reqs.keys() {
if let Some(cycle) = self.cycle_visit(name, &mut visited, &mut stack, &mut path) {
return Some(cycle);
}
}
None
}
fn cycle_visit(
&self,
name: &str,
visited: &mut HashSet<String>,
stack: &mut HashSet<String>,
path: &mut Vec<String>,
) -> Option<Vec<String>> {
if stack.contains(name) {
let idx = path.iter().position(|n| n == name).unwrap();
return Some(path[idx..].to_vec());
}
if visited.contains(name) {
return None;
}
visited.insert(name.to_string());
stack.insert(name.to_string());
path.push(name.to_string());
if let Some(req) = self.reqs.get(name) {
for prereq in req.prereqs.iter().flat_map(PrereqGroup::alternatives) {
if let Some(cycle) = self.cycle_visit(prereq, visited, stack, path) {
return Some(cycle);
}
}
}
stack.remove(name);
path.pop();
None
}
}
impl Default for ReqTree {
fn default() -> Self {
Self::new()
}
}