dep_tree/
lib.rs

1use std::{
2  cell::RefCell,
3  collections::{btree_map::Entry, BTreeMap, BTreeSet},
4  rc::Rc,
5};
6use thiserror::Error;
7
8pub type DepId = (u64, usize);
9
10#[derive(Clone, Debug, Error)]
11pub enum DepTreeBuilderError {
12  #[error("unit `{0:?}` depends on itself")]
13  SelfDependency(DepId),
14  #[error("unit `{0:?}` recurses when depending on `{1:?}`, `{2}`")]
15  CircularDependency(DepId, DepId, String),
16}
17
18pub type DepTreeBuilderResult<T> = Result<T, DepTreeBuilderError>;
19
20#[derive(Clone, Debug, Default)]
21pub struct DepTreeBuilder {
22  inner: Rc<RefCell<Box<BTreeMap<DepId, Vec<DepId>>>>>,
23}
24
25impl DepTreeBuilder {
26  pub fn new() -> Self {
27    Self::default()
28  }
29
30  pub fn with_dep(&mut self, id: DepId, deps: Vec<DepId>) -> Self {
31    let mut inner_lock = self.inner.try_borrow_mut().unwrap();
32    match inner_lock.entry(id) {
33      Entry::Vacant(entry) => {
34        entry.insert(deps);
35      }
36      Entry::Occupied(mut entry) => {
37        entry.get_mut().extend(deps);
38      }
39    }
40    self.clone()
41  }
42  
43  pub fn build(self) -> DepTreeBuilderResult<Box<DepTree>> {
44    let inner = self.inner.try_borrow().unwrap();
45    let (mut visited, mut resolved): (
46      Vec<DepId>,
47      BTreeMap<DepId, Vec<DepId>>,
48    ) = (
49      Vec::new(),
50      BTreeMap::new(),
51    );
52    for (unit, deps) in inner.clone().into_iter() {
53      if deps.contains(&unit) {
54        return Err(DepTreeBuilderError::SelfDependency(unit));
55      }
56      let mut stack = Vec::new();
57      if self.has_circular_dependency(unit, &inner, &mut visited, &mut stack) {
58        return Err(DepTreeBuilderError::CircularDependency(
59          *stack.first().unwrap(),
60          *stack.last().unwrap(),
61          stack
62            .iter()
63            .map(|(id, version)| format!("({id}, {version})"))
64            .collect::<Vec<_>>()
65            .join(" -> ")
66        ));
67      }
68      resolved.insert(unit, deps);
69    }
70    Ok(Box::new(DepTree::new(Rc::new(resolved))))
71  }
72
73  fn has_circular_dependency(
74    &self,
75    unit: DepId,
76    tree: &BTreeMap<DepId, Vec<DepId>>,
77    visited: &mut Vec<DepId>,
78    stack: &mut Vec<DepId>,
79  ) -> bool {
80    if visited.contains(&unit) {
81      return false;
82    }
83    if stack.contains(&unit) {
84      return true;
85    }
86    stack.push(unit);
87    if let Some(deps) = tree.get(&unit) {
88      for &dep in deps {
89        if self.has_circular_dependency(dep, tree, visited, stack) {
90          return true;
91        }
92      }
93    }
94    stack.pop();
95    visited.push(unit);
96    false
97  }
98}
99
100#[derive(Clone, Debug, Default)]
101pub struct DepTree {
102  inner: Rc<BTreeMap<DepId, Vec<DepId>>>,
103}
104
105impl DepTree {
106  pub fn new(inner: Rc<BTreeMap<DepId, Vec<DepId>>>) -> Self {
107    Self { inner }
108  }
109  
110  pub fn most_dependencies(&self) -> Vec<(DepId, usize)> {
111    let mut dependency_counts = self.inner.keys().map(|id| {
112      let count = self.count_dependencies(id, &mut BTreeSet::new());
113      (*id, count)
114    }).collect::<Vec<_>>();
115
116    dependency_counts.sort_by(|a, b| b.1.cmp(&a.1));
117    dependency_counts
118  }
119
120  pub fn most_dependents(&self) -> Vec<(DepId, usize)> {
121    let mut dependent_counts = self.calculate_dependents();
122    dependent_counts.sort_by(|a, b| b.1.cmp(&a.1));
123    dependent_counts
124  }
125
126  pub fn least_dependencies(&self) -> Vec<(DepId, usize)> {
127    let mut dependency_counts = self.inner.keys().map(|id| {
128      let count = self.count_dependencies(id, &mut BTreeSet::new());
129      (*id, count)
130    }).collect::<Vec<_>>();
131
132    dependency_counts.sort_by(|a, b| a.1.cmp(&b.1));
133    dependency_counts
134  }
135
136  pub fn least_dependents(&self) -> Vec<(DepId, usize)> {
137    let mut dependent_counts = self.calculate_dependents();
138    dependent_counts.sort_by(|a, b| a.1.cmp(&b.1));
139    dependent_counts
140  }
141
142  pub fn dependencies_of(&self, unit: DepId) -> Vec<DepId> {
143    let mut visited = BTreeSet::new();
144    let mut dependencies = Vec::new();
145    self.collect_dependencies(&unit, &mut visited, &mut dependencies);
146    dependencies
147  }
148
149  pub fn dependents_of(&self, unit: DepId) -> Vec<DepId> {
150    self.inner
151      .iter()
152      .filter_map(|(&key, deps)| {
153        if deps.contains(&unit) {
154          Some(key)
155        } else {
156          None
157        }
158      })
159      .collect()
160  }
161
162  fn count_dependencies(&self, id: &DepId, visited: &mut BTreeSet<DepId>) -> usize {
163    if !visited.insert(*id) {
164      return 0;
165    }
166    self.inner
167      .get(id)
168      .map(|deps| {
169        deps
170          .iter()
171          .map(|dep| 1 + self.count_dependencies(dep, visited))
172          .sum()
173      })
174      .unwrap_or(0)
175  }
176
177  fn collect_dependencies(&self, id: &DepId, visited: &mut BTreeSet<DepId>, dependencies: &mut Vec<DepId>) {
178    if !visited.insert(*id) {
179      return;
180    }
181    if let Some(deps) = self.inner.get(id) {
182      for dep in deps {
183        dependencies.push(*dep);
184        self.collect_dependencies(dep, visited, dependencies);
185      }
186    }
187  }
188
189  fn calculate_dependents(&self) -> Vec<(DepId, usize)> {
190    let mut dependent_map: BTreeMap<DepId, usize> = BTreeMap::new();
191    
192    for (&key, deps) in self.inner.iter() {
193      for &dep in deps {
194        *dependent_map.entry(dep).or_insert(0) += 1;
195      }
196      dependent_map.entry(key).or_insert(0);
197    }
198
199    dependent_map.into_iter().collect()
200  }
201}