1crate::ix!();
3
4#[derive(Debug)]
5pub struct WorkspaceDependencyTree {
6 roots: Vec<WorkspaceTreeNode>,
9}
10
11#[derive(Debug)]
12pub struct WorkspaceTreeNode {
13 crate_name: String,
14 crate_version: Option<SemverVersion>,
15 crate_path: PathBuf,
16 children: Vec<WorkspaceTreeNode>,
17}
18
19impl WorkspaceDependencyTree {
20 pub fn new(roots: Vec<WorkspaceTreeNode>) -> Self {
22 Self { roots }
23 }
24
25 pub fn is_empty(&self) -> bool {
27 self.roots.is_empty()
28 }
29
30 pub fn render(
33 &self,
34 show_version: bool,
35 show_path: bool,
36 ) -> String {
37 if self.is_empty() {
38 return "(no crates in workspace)".to_string();
39 }
40
41 let mut out = String::new();
42 for (i, root) in self.roots.iter().enumerate() {
43 root.render_recursive(0, show_version, show_path, &mut out);
45 if i + 1 < self.roots.len() {
46 out.push('\n'); }
48 }
49 out
50 }
51}
52
53impl WorkspaceTreeNode {
54 pub fn new(
56 crate_name: String,
57 crate_version: Option<SemverVersion>,
58 crate_path: PathBuf,
59 ) -> Self {
60 Self {
61 crate_name,
62 crate_version,
63 crate_path,
64 children: Vec::new(),
65 }
66 }
67
68 pub fn add_child(&mut self, child: WorkspaceTreeNode) {
70 self.children.push(child);
71 }
72
73 pub fn render_recursive(
76 &self,
77 level: usize,
78 show_version: bool,
79 show_path: bool,
80 out: &mut String,
81 ) {
82 let indent = " ".repeat(level);
84
85 let mut line = format!("{}{}", indent, self.crate_name);
87
88 if show_version {
89 if let Some(ref v) = self.crate_version {
90 line.push_str(&format!(" (v{})", v));
91 }
92 }
93
94 if show_path {
95 line.push_str(&format!(" [{}]", self.crate_path.display()));
96 }
97
98 out.push_str(&line);
99 out.push('\n');
100
101 for child in &self.children {
103 child.render_recursive(level + 1, show_version, show_path, out);
104 }
105 }
106}
107
108#[async_trait]
109pub trait WorkspaceTreeBuilder {
110 async fn build_workspace_tree(
113 &self,
114 levels: usize,
115 verbose: bool,
116 ) -> Result<WorkspaceDependencyTree, WorkspaceError>;
117}
118
119#[async_trait]
123impl<P,H> WorkspaceTreeBuilder for Workspace<P,H>
124where
125 P: From<PathBuf> + AsRef<Path> + Send + Sync + 'static,
126 H: CrateHandleInterface<P> + Send + Sync + 'static,
127{
128 async fn build_workspace_tree(
129 &self,
130 levels: usize,
131 verbose: bool,
132 ) -> Result<WorkspaceDependencyTree, WorkspaceError>
133 {
134 let all_crates = self.crates();
136 let mut graph = Graph::<String, ()>::new();
140 let mut name_to_idx = BTreeMap::new();
141
142 let mut crate_info: Vec<(String, Option<SemverVersion>, PathBuf, Vec<String>)> = Vec::new();
145
146 for arc_h in all_crates {
148 let guard = arc_h.lock().await;
149
150 let crate_name = guard.name().to_string();
151
152 let crate_version = Some(guard.version()?);
155
156 let crate_path: PathBuf = guard.as_ref().to_path_buf();
159
160 let internal_deps: Vec<String> = guard.internal_dependencies().await?;
162
163 let node_idx = graph.add_node(crate_name.clone());
165 name_to_idx.insert(crate_name.clone(), node_idx);
166
167 crate_info.push((crate_name, crate_version, crate_path, internal_deps));
169 }
170
171 for (crate_name, _v, _path, deps) in &crate_info {
173 let src_idx = name_to_idx[crate_name];
174 for dep_name in deps {
175 if let Some(&dst_idx) = name_to_idx.get(dep_name) {
176 graph.add_edge(src_idx, dst_idx, ());
177 }
178 }
179 }
180
181 let mut indeg = BTreeMap::new();
183 for n in graph.node_indices() {
184 indeg.insert(n, 0usize);
185 }
186 for e in graph.edge_indices() {
187 let (s, d) = graph.edge_endpoints(e).unwrap();
188 *indeg.get_mut(&d).unwrap() += 1;
190 }
191
192 let roots: Vec<_> = indeg
193 .iter()
194 .filter_map(|(node_idx, count)| {
195 if *count == 0 {
196 Some(*node_idx)
197 } else {
198 None
199 }
200 })
201 .collect();
202
203 let mut out_roots = Vec::new();
205
206 for root_idx in roots {
207 let crate_name = graph[root_idx].clone(); let (version, path) = crate_info_for_name(&crate_info, &crate_name);
209
210 let mut root_node = WorkspaceTreeNode::new(
211 crate_name,
212 version,
213 path,
214 );
215
216 if levels > 0 {
218 build_children_rec(
219 &graph,
220 &crate_info,
221 root_idx,
222 1,
223 levels,
224 &mut root_node
225 );
226 }
227
228 out_roots.push(root_node);
229 }
230
231 let tree = WorkspaceDependencyTree::new(out_roots);
232 Ok(tree)
233 }
234}
235
236fn crate_info_for_name(
238 crate_info: &[(String, Option<SemverVersion>, PathBuf, Vec<String>)],
239 name: &str,
240) -> (Option<SemverVersion>, PathBuf)
241{
242 for (cn, cv, p, _) in crate_info {
243 if cn == name {
244 return (cv.clone(), p.clone());
245 }
246 }
247 (None, PathBuf::new())
249}
250
251fn build_children_rec(
253 graph: &Graph<String, ()>,
254 crate_info: &[(String, Option<SemverVersion>, PathBuf, Vec<String>)],
255 node_idx: petgraph::graph::NodeIndex,
256 current_level: usize,
257 max_levels: usize,
258 parent_node: &mut WorkspaceTreeNode,
259) {
260 if current_level >= max_levels {
261 return; }
263
264 for edge in graph.edges_directed(node_idx, petgraph::Direction::Outgoing) {
266 let child_idx = edge.target();
267 let crate_name = &graph[child_idx];
268
269 let (version, path) = crate_info_for_name(crate_info, crate_name);
270
271 let mut child_node = WorkspaceTreeNode::new(
272 crate_name.to_string(),
273 version,
274 path,
275 );
276
277 build_children_rec(
279 graph, crate_info,
280 child_idx,
281 current_level + 1,
282 max_levels,
283 &mut child_node,
284 );
285
286 parent_node.add_child(child_node);
287 }
288}