Skip to main content

chaud_hot/cargo/
metadata.rs

1use super::Cargo;
2use crate::cargo::StdioMode;
3use crate::util::CommandExt as _;
4use anyhow::{Context as _, Result};
5use camino::{Utf8Path, Utf8PathBuf};
6use core::fmt;
7use core::str::Chars;
8use nanoserde::{DeJson, DeJsonErr, DeJsonState, DeJsonTok};
9
10#[derive(Debug, DeJson)]
11pub struct Metadata {
12    packages: Vec<Package>,
13}
14
15impl Cargo {
16    pub fn load_metadata(&self) -> Result<Metadata> {
17        let buf = run_cargo(self).context("Failed to run `cargo metadata`")?;
18        Metadata::deserialize_json(&buf).context("Failed to parse `cargo metadata` output")
19    }
20}
21
22impl Metadata {
23    pub fn packages(&self) -> &[Package] {
24        &self.packages
25    }
26}
27#[derive(Debug, DeJson)]
28pub struct Package {
29    name: PackageName,
30    manifest_path: ManifestPath,
31    dependencies: Vec<Dependency>,
32    targets: Vec<Target>,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct ManifestPath(Utf8PathBuf);
37
38impl DeJson for ManifestPath {
39    fn de_json(s: &mut DeJsonState, i: &mut Chars) -> Result<Self, DeJsonErr> {
40        s.string(i)?;
41        Ok(ManifestPath(s.strbuf.clone().into()))
42    }
43}
44
45impl ManifestPath {
46    pub fn new(path: impl Into<Utf8PathBuf>) -> Self {
47        Self(path.into())
48    }
49
50    pub fn path(&self) -> &Utf8Path {
51        &self.0
52    }
53}
54
55impl Package {
56    pub fn name(&self) -> &PackageName {
57        &self.name
58    }
59
60    pub fn manifest_path(&self) -> &ManifestPath {
61        &self.manifest_path
62    }
63
64    pub fn dependencies(&self) -> &[Dependency] {
65        &self.dependencies
66    }
67
68    pub fn targets(&self) -> &[Target] {
69        &self.targets
70    }
71}
72
73#[derive(Debug, DeJson, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
74#[nserde(transparent)]
75pub struct PackageName(String);
76
77impl fmt::Display for PackageName {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "`{}`", self.0)
80    }
81}
82
83#[derive(Debug, DeJson)]
84pub struct Dependency {
85    name: PackageName,
86    kind: DependencyKind,
87}
88
89impl Dependency {
90    pub fn name(&self) -> &PackageName {
91        &self.name
92    }
93
94    pub fn kind(&self) -> DependencyKind {
95        self.kind
96    }
97}
98
99#[derive(Debug, Copy, Clone, PartialEq)]
100pub enum DependencyKind {
101    Normal,
102    Build,
103    Other,
104}
105
106impl DeJson for DependencyKind {
107    fn de_json(s: &mut DeJsonState, i: &mut Chars) -> Result<Self, DeJsonErr> {
108        if s.tok == DeJsonTok::Null {
109            s.next_tok(i)?;
110            return Ok(Self::Normal);
111        }
112        s.string(i)?;
113        match s.strbuf.as_ref() {
114            "build" => Ok(Self::Build),
115            _ => Ok(Self::Other),
116        }
117    }
118}
119
120#[derive(Debug, DeJson)]
121pub struct Target {
122    name: TargetName,
123    kind: Vec<TargetKind>,
124    #[nserde(proxy = "String")]
125    src_path: Utf8PathBuf,
126}
127
128impl Target {
129    pub fn name(&self) -> &TargetName {
130        &self.name
131    }
132
133    pub fn kind(&self) -> &[TargetKind] {
134        &self.kind
135    }
136
137    pub fn src_path(&self) -> &Utf8Path {
138        &self.src_path
139    }
140}
141
142#[derive(Debug, DeJson, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
143#[nserde(transparent)]
144pub struct TargetName(String);
145
146impl PartialEq<str> for TargetName {
147    fn eq(&self, other: &str) -> bool {
148        self.0 == other
149    }
150}
151
152impl TargetName {
153    pub fn as_str(&self) -> &str {
154        &self.0
155    }
156}
157
158#[derive(Debug, Copy, Clone, PartialEq)]
159pub enum TargetKind {
160    Bin,
161    Lib,
162    RLib,
163    CustomBuild,
164    ProcMacro,
165    Other,
166}
167
168impl DeJson for TargetKind {
169    fn de_json(s: &mut DeJsonState, i: &mut Chars) -> Result<Self, DeJsonErr> {
170        s.string(i)?;
171        match s.strbuf.as_ref() {
172            "bin" => Ok(Self::Bin),
173            "lib" => Ok(Self::Lib),
174            "rlib" => Ok(Self::RLib),
175            "custom-build" => Ok(Self::CustomBuild),
176            "proc-macro" => Ok(Self::ProcMacro),
177            _ => Ok(Self::Other),
178        }
179    }
180}
181
182fn run_cargo(cargo: &Cargo) -> Result<String> {
183    let mut cmd = cargo.cmd("metadata", StdioMode::LoudCapture);
184
185    cmd.args(["--format-version=1", "--no-deps"]);
186
187    log::trace!("Running {cmd:?}");
188
189    let output = cmd.stdout_str()?;
190
191    Ok(output)
192}