chaud_hot/cargo/
metadata.rs1use 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}