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}