1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Copyright (C) 2026 by GiGa infosystems
//! Run `cargo metadata` & index the result by [`PackageId`]s
use std::{collections::HashMap, path::Path};
use crate::Platform;
use camino::Utf8PathBuf;
use cargo_metadata::{MetadataCommand, Node, Package, PackageId};
use color_eyre::Result;
/// The indexed output of `cargo metadata`
#[derive(Debug)]
pub struct IndexedMetadata {
/// The platform `cargo metadata` ran for (via `--filter-platform`).
///
/// If it is `None`, this contains all packages for all platforms.
pub platform: Option<Platform>,
/// The packages indexed by [`PackageId`]s
pub packages: HashMap<PackageId, Package>,
/// The resolution graph indexed by [`PackageId`]s
pub resolve: HashMap<PackageId, Node>,
/// The root directory of the workspace (the directory containing the manifest)
pub workspace_root: Utf8PathBuf,
/// The list of members in this workspace
pub workspace_members: Vec<PackageId>,
/// The default members of this workspace. Contrary to [`cargo_metadata`], this is represented
/// as an `Option` instead of panicking on access if it was missing.
pub workspace_default_members: Option<Vec<PackageId>>,
}
impl IndexedMetadata {
/// Gather & index dependency metadata for the `Cargo.toml` at `path` (for `--manifest-path`),
/// with the given platform (via `--filter-platform`).
///
/// If `platform` is `None`, this contains all packages for all platforms.
pub fn gather(path: &Path, platform: Option<Platform>) -> Result<Self> {
let mut other_options = Vec::new();
if let Some(ref platform) = platform {
other_options.extend(["--filter-platform".to_owned(), platform.0.clone()]);
}
other_options.push("--locked".to_owned());
let data = MetadataCommand::new()
.manifest_path(path)
.other_options(other_options)
.exec()?;
let packages = data
.packages
.into_iter()
.map(|pkg| (pkg.id.clone(), pkg))
.collect();
let resolve = data.resolve.map_or_else(HashMap::new, |resolve| {
resolve
.nodes
.into_iter()
.map(|node| (node.id.clone(), node))
.collect()
});
let workspace_default_members = data
.workspace_default_members
.is_available()
.then(|| (*data.workspace_default_members).to_owned());
Ok(IndexedMetadata {
platform,
packages,
resolve,
workspace_root: data.workspace_root,
workspace_members: data.workspace_members,
workspace_default_members,
})
}
/// Return the default members, or if they are missing, all workspace members
pub fn get_workspace_default_members(&self) -> &[PackageId] {
self.workspace_default_members
.as_ref()
.unwrap_or(self.workspace_members.as_ref())
}
}