diff --git a/dev-tools/ls-apis/src/cargo.rs b/dev-tools/ls-apis/src/cargo.rs index eeb2316d820..7b851fc75cc 100644 --- a/dev-tools/ls-apis/src/cargo.rs +++ b/dev-tools/ls-apis/src/cargo.rs @@ -234,23 +234,16 @@ impl Workspace { Ok(path) } - /// Iterate over the required dependencies of package `root`, invoking - /// `func` for each one as: + /// Walks the required (normal and build) dependencies of package `root`. /// - /// ```ignore - /// func(package: &Package, dep_path: &DepPath) - /// ``` - /// - /// where `package` is the package that is (directly or indirectly) a - /// dependency of `root` and `dep_path` describes the dependency path from - /// `root` to `package`. - pub fn walk_required_deps_recursively( - &self, + /// Returns a [`WalkOutcome`] describing every package reachable from + /// `root`, each paired with a dependency path to it. + pub fn walk_required_deps_recursively<'a>( + &'a self, root: &Package, - func: &mut dyn FnMut(&Package, &DepPath), - ) -> Result<()> { - struct Remaining<'a> { - node: &'a cargo_metadata::Node, + ) -> Result> { + struct Remaining<'n> { + node: &'n cargo_metadata::Node, path: DepPath, } @@ -268,6 +261,7 @@ impl Workspace { path: DepPath::for_pkg(root.id.clone()), }]; let mut seen: BTreeSet = BTreeSet::new(); + let mut found: Vec<(&'a Package, DepPath)> = Vec::new(); while let Some(Remaining { node: next, path }) = remaining.pop() { for d in &next.deps { @@ -288,7 +282,7 @@ impl Workspace { // package metadata. let dep_pkg = self.packages_by_id.get(did).unwrap(); let dep_node = self.nodes_by_id.get(did).unwrap(); - func(dep_pkg, &path); + found.push((dep_pkg, path.clone())); if seen.contains(did) { continue; } @@ -299,7 +293,7 @@ impl Workspace { } } - Ok(()) + Ok(WalkOutcome { found }) } /// Return all package ids for the given `pkgname` @@ -343,6 +337,16 @@ fn cargo_toml_parent( Ok(path) } +/// The result of [`Workspace::walk_required_deps_recursively`]. +pub struct WalkOutcome<'a> { + /// Every package encountered as a required (normal or build) dependency of + /// the walk's `root`, each paired with a dependency path from `root` to + /// that package. + /// + /// A package reachable by more than one path appears once per path. + pub found: Vec<(&'a Package, DepPath)>, +} + /// Describes a "dependency path": a path through the Cargo dependency graph /// from one package to another, which describes how one package depends on /// another diff --git a/dev-tools/ls-apis/src/system_apis.rs b/dev-tools/ls-apis/src/system_apis.rs index 01e618c84de..2680ca34914 100644 --- a/dev-tools/ls-apis/src/system_apis.rs +++ b/dev-tools/ls-apis/src/system_apis.rs @@ -22,7 +22,6 @@ use crate::workspaces::Workspaces; use anyhow::Result; use anyhow::{Context, anyhow, bail}; use camino::Utf8PathBuf; -use cargo_metadata::Package; use iddqd::IdOrdItem; use iddqd::IdOrdMap; use iddqd::id_upcast; @@ -175,12 +174,11 @@ impl SystemApis { let dep_path = DepPath::for_pkg(server_pkg.id.clone()); tracker.found_package(dunit_pkg, dunit_pkg, &dep_path); - workspace.walk_required_deps_recursively( - server_pkg, - &mut |p: &Package, dep_path: &DepPath| { - tracker.found_package(dunit_pkg, &p.name, dep_path); - }, - )?; + let outcome = + workspace.walk_required_deps_recursively(server_pkg)?; + for (dep_pkg, dep_path) in &outcome.found { + tracker.found_package(dunit_pkg, &dep_pkg.name, dep_path); + } } } @@ -219,17 +217,8 @@ impl SystemApis { for server_pkgname in server_component_units.keys() { let (workspace, pkg) = workspaces.find_package_workspace(server_pkgname)?; - workspace - .walk_required_deps_recursively( - pkg, - &mut |p: &Package, dep_path: &DepPath| { - deps_tracker.found_dependency( - server_pkgname, - &p.name, - dep_path, - ); - }, - ) + let outcome = workspace + .walk_required_deps_recursively(pkg) .with_context(|| { format!( "iterating dependencies of workspace {:?} package {:?}", @@ -237,6 +226,13 @@ impl SystemApis { server_pkgname ) })?; + for (dep_pkg, dep_path) in &outcome.found { + deps_tracker.found_dependency( + server_pkgname, + &dep_pkg.name, + dep_path, + ); + } } let (apis_consumed, api_consumers) =