workspacer_tree/build_workspace_tree.rs
1// ---------------- [ File: workspacer-tree/src/build_workspace_tree.rs ]
2crate::ix!();
3
4#[async_trait]
5pub trait BuildWorkspaceTree {
6 /// Build the workspace dependency tree up to `levels` levels deep,
7 /// optionally toggling “verbose,” or ignoring it as desired.
8 async fn build_workspace_tree(
9 &self,
10 levels: usize,
11 verbose: bool,
12 ) -> Result<WorkspaceDependencyTree, WorkspaceError>;
13}
14
15// We need to ensure P, H meet the strict bounds that your Workspace<P,H> requires:
16// P: From<PathBuf> + AsRef<Path> + Send + Sync + 'static
17// H: CrateHandleInterface<P> + Send + Sync + 'static
18#[async_trait]
19impl<P,H> BuildWorkspaceTree for Workspace<P,H>
20where
21 P: From<PathBuf> + AsRef<Path> + Send + Sync + 'static,
22 H: CrateHandleInterface<P> + Send + Sync + 'static,
23{
24 async fn build_workspace_tree(
25 &self,
26 levels: usize,
27 verbose: bool,
28 ) -> Result<WorkspaceDependencyTree, WorkspaceError>
29 {
30 // 1) We gather all crates in the workspace:
31 let all_crates = self.crates();
32 // ^ typically returns e.g. &Vec<Arc<AsyncMutex<H>>>, or an iterator
33
34 // Build a petgraph with (node=crate name), (edge=dependency).
35 let mut graph = Graph::<String, ()>::new();
36 let mut name_to_idx = BTreeMap::new();
37
38 // For storing info about each crate => version + path + internal deps
39 // We'll store Option<SemverVersion> for the crate_version, plus the PathBuf
40 let mut crate_info: Vec<(String, Option<SemverVersion>, PathBuf, Vec<String>)> = Vec::new();
41
42 // Lock each crate, gather metadata
43 for arc_h in all_crates {
44 let guard = arc_h.lock().await;
45
46 let crate_name = guard.name().to_string();
47
48 // Suppose `guard.version()?` yields an Option<SemverVersion> or a SemverVersion?
49 // If it yields `SemverVersion`, wrap it in Some(...). If it can be None, store that.
50 let crate_version = Some(guard.version()?);
51
52 // Suppose guard.as_ref() yields a P that is basically a Path or something?
53 // We'll get a PathBuf from it:
54 let crate_path: PathBuf = guard.as_ref().to_path_buf();
55
56 // We also gather “internal_deps,” i.e. other crates in the workspace.
57 let internal_deps: Vec<String> = guard.internal_dependencies().await?;
58
59 // Add node to the graph
60 let node_idx = graph.add_node(crate_name.clone());
61 name_to_idx.insert(crate_name.clone(), node_idx);
62
63 // Put in crate_info for later lookup
64 crate_info.push((crate_name, crate_version, crate_path, internal_deps));
65 }
66
67 // 2) Add edges
68 for (crate_name, _v, _path, deps) in &crate_info {
69 let src_idx = name_to_idx[crate_name];
70 for dep_name in deps {
71 if let Some(&dst_idx) = name_to_idx.get(dep_name) {
72 graph.add_edge(src_idx, dst_idx, ());
73 }
74 }
75 }
76
77 // 3) Identify “roots” => nodes with no incoming edges
78 let mut indeg = BTreeMap::new();
79 for n in graph.node_indices() {
80 indeg.insert(n, 0usize);
81 }
82 for e in graph.edge_indices() {
83 let (s, d) = graph.edge_endpoints(e).unwrap();
84 // increment the in-degree of d
85 *indeg.get_mut(&d).unwrap() += 1;
86 }
87
88 let roots: Vec<_> = indeg
89 .iter()
90 .filter_map(|(node_idx, count)| {
91 if *count == 0 {
92 Some(*node_idx)
93 } else {
94 None
95 }
96 })
97 .collect();
98
99 // 4) Build the “WorkspaceDependencyTree”
100 let mut out_roots = Vec::new();
101
102 for root_idx in roots {
103 let crate_name = graph[root_idx].clone(); // The node’s name
104 let (version, path) = crate_info_for_name(&crate_info, &crate_name);
105
106 let mut root_node = WorkspaceTreeNode::new(
107 crate_name,
108 version,
109 path,
110 );
111
112 // Recursively gather children (if levels>0)
113 if levels > 0 {
114 build_children_rec(
115 &graph,
116 &crate_info,
117 root_idx,
118 1,
119 levels,
120 &mut root_node
121 );
122 }
123
124 out_roots.push(root_node);
125 }
126
127 let tree = WorkspaceDependencyTree::new(out_roots);
128 Ok(tree)
129 }
130}