]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
1 | #![deny(missing_docs)] |
2 | //! Structured access to the output of `cargo metadata` and `cargo --message-format=json`. | |
3 | //! Usually used from within a `cargo-*` executable | |
4 | //! | |
5 | //! See the [cargo book](https://doc.rust-lang.org/cargo/index.html) for | |
6 | //! details on cargo itself. | |
7 | //! | |
8 | //! ## Examples | |
9 | //! | |
ba9703b0 | 10 | //! ```rust |
ba9703b0 XL |
11 | //! # extern crate cargo_metadata; |
12 | //! # use std::path::Path; | |
13 | //! let mut args = std::env::args().skip_while(|val| !val.starts_with("--manifest-path")); | |
14 | //! | |
15 | //! let mut cmd = cargo_metadata::MetadataCommand::new(); | |
16 | //! let manifest_path = match args.next() { | |
17 | //! Some(ref p) if p == "--manifest-path" => { | |
18 | //! cmd.manifest_path(args.next().unwrap()); | |
19 | //! } | |
20 | //! Some(p) => { | |
21 | //! cmd.manifest_path(p.trim_start_matches("--manifest-path=")); | |
22 | //! } | |
23 | //! None => {} | |
24 | //! }; | |
25 | //! | |
26 | //! let _metadata = cmd.exec().unwrap(); | |
27 | //! ``` | |
28 | //! | |
ba9703b0 XL |
29 | //! Pass features flags |
30 | //! | |
31 | //! ```rust | |
32 | //! # // This should be kept in sync with the equivalent example in the readme. | |
33 | //! # extern crate cargo_metadata; | |
34 | //! # use std::path::Path; | |
35 | //! # fn main() { | |
36 | //! use cargo_metadata::{MetadataCommand, CargoOpt}; | |
37 | //! | |
38 | //! let _metadata = MetadataCommand::new() | |
39 | //! .manifest_path("./Cargo.toml") | |
40 | //! .features(CargoOpt::AllFeatures) | |
41 | //! .exec() | |
42 | //! .unwrap(); | |
43 | //! # } | |
44 | //! ``` | |
45 | //! | |
46 | //! Parse message-format output: | |
47 | //! | |
48 | //! ``` | |
49 | //! # extern crate cargo_metadata; | |
50 | //! use std::process::{Stdio, Command}; | |
51 | //! use cargo_metadata::Message; | |
52 | //! | |
53 | //! let mut command = Command::new("cargo") | |
54 | //! .args(&["build", "--message-format=json"]) | |
55 | //! .stdout(Stdio::piped()) | |
56 | //! .spawn() | |
57 | //! .unwrap(); | |
58 | //! | |
f035d41b XL |
59 | //! let reader = std::io::BufReader::new(command.stdout.take().unwrap()); |
60 | //! for message in cargo_metadata::Message::parse_stream(reader) { | |
ba9703b0 XL |
61 | //! match message.unwrap() { |
62 | //! Message::CompilerMessage(msg) => { | |
63 | //! println!("{:?}", msg); | |
64 | //! }, | |
65 | //! Message::CompilerArtifact(artifact) => { | |
66 | //! println!("{:?}", artifact); | |
67 | //! }, | |
68 | //! Message::BuildScriptExecuted(script) => { | |
69 | //! println!("{:?}", script); | |
70 | //! }, | |
f035d41b XL |
71 | //! Message::BuildFinished(finished) => { |
72 | //! println!("{:?}", finished); | |
73 | //! }, | |
ba9703b0 XL |
74 | //! _ => () // Unknown message |
75 | //! } | |
76 | //! } | |
77 | //! | |
78 | //! let output = command.wait().expect("Couldn't get cargo's exit status"); | |
79 | //! ``` | |
80 | ||
ba9703b0 XL |
81 | use std::collections::HashMap; |
82 | use std::env; | |
83 | use std::fmt; | |
f035d41b | 84 | use std::path::PathBuf; |
ba9703b0 XL |
85 | use std::process::Command; |
86 | use std::str::from_utf8; | |
87 | ||
3dfed10e | 88 | pub use semver::Version; |
ba9703b0 XL |
89 | |
90 | pub use dependency::{Dependency, DependencyKind}; | |
91 | use diagnostic::Diagnostic; | |
92 | pub use errors::{Error, Result}; | |
f035d41b XL |
93 | #[allow(deprecated)] |
94 | pub use messages::parse_messages; | |
3dfed10e XL |
95 | pub use messages::{ |
96 | Artifact, ArtifactProfile, BuildFinished, BuildScript, CompilerMessage, Message, MessageIter, | |
97 | }; | |
98 | use serde::{Deserialize, Serialize}; | |
ba9703b0 XL |
99 | |
100 | mod dependency; | |
101 | pub mod diagnostic; | |
102 | mod errors; | |
103 | mod messages; | |
104 | ||
105 | /// An "opaque" identifier for a package. | |
106 | /// It is possible to inspect the `repr` field, if the need arises, but its | |
107 | /// precise format is an implementation detail and is subject to change. | |
108 | /// | |
109 | /// `Metadata` can be indexed by `PackageId`. | |
110 | #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
111 | #[serde(transparent)] | |
112 | pub struct PackageId { | |
113 | /// The underlying string representation of id. | |
114 | pub repr: String, | |
115 | } | |
116 | ||
117 | impl std::fmt::Display for PackageId { | |
118 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
119 | fmt::Display::fmt(&self.repr, f) | |
120 | } | |
121 | } | |
122 | ||
123 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
124 | /// Starting point for metadata returned by `cargo metadata` | |
125 | pub struct Metadata { | |
126 | /// A list of all crates referenced by this crate (and the crate itself) | |
127 | pub packages: Vec<Package>, | |
128 | /// A list of all workspace members | |
129 | pub workspace_members: Vec<PackageId>, | |
130 | /// Dependencies graph | |
131 | pub resolve: Option<Resolve>, | |
132 | /// Workspace root | |
133 | pub workspace_root: PathBuf, | |
134 | /// Build directory | |
135 | pub target_directory: PathBuf, | |
136 | version: usize, | |
137 | #[doc(hidden)] | |
138 | #[serde(skip)] | |
139 | __do_not_match_exhaustively: (), | |
140 | } | |
141 | ||
142 | impl<'a> std::ops::Index<&'a PackageId> for Metadata { | |
143 | type Output = Package; | |
144 | ||
145 | fn index(&self, idx: &'a PackageId) -> &Package { | |
146 | self.packages | |
147 | .iter() | |
148 | .find(|p| p.id == *idx) | |
149 | .unwrap_or_else(|| panic!("no package with this id: {:?}", idx)) | |
150 | } | |
151 | } | |
152 | ||
153 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
154 | /// A dependency graph | |
155 | pub struct Resolve { | |
156 | /// Nodes in a dependencies graph | |
157 | pub nodes: Vec<Node>, | |
158 | ||
159 | /// The crate for which the metadata was read. | |
160 | pub root: Option<PackageId>, | |
161 | #[doc(hidden)] | |
162 | #[serde(skip)] | |
163 | __do_not_match_exhaustively: (), | |
164 | } | |
165 | ||
166 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
167 | /// A node in a dependencies graph | |
168 | pub struct Node { | |
169 | /// An opaque identifier for a package | |
170 | pub id: PackageId, | |
171 | /// Dependencies in a structured format. | |
172 | /// | |
173 | /// `deps` handles renamed dependencies whereas `dependencies` does not. | |
174 | #[serde(default)] | |
175 | pub deps: Vec<NodeDep>, | |
176 | ||
177 | /// List of opaque identifiers for this node's dependencies. | |
178 | /// It doesn't support renamed dependencies. See `deps`. | |
179 | pub dependencies: Vec<PackageId>, | |
180 | ||
181 | /// Features enabled on the crate | |
182 | #[serde(default)] | |
183 | pub features: Vec<String>, | |
184 | #[doc(hidden)] | |
185 | #[serde(skip)] | |
186 | __do_not_match_exhaustively: (), | |
187 | } | |
188 | ||
189 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
190 | /// A dependency in a node | |
191 | pub struct NodeDep { | |
192 | /// The name of the dependency's library target. | |
193 | /// If the crate was renamed, it is the new name. | |
194 | pub name: String, | |
195 | /// Package ID (opaque unique identifier) | |
196 | pub pkg: PackageId, | |
197 | /// The kinds of dependencies. | |
198 | /// | |
199 | /// This field was added in Rust 1.41. | |
200 | #[serde(default)] | |
201 | pub dep_kinds: Vec<DepKindInfo>, | |
202 | #[doc(hidden)] | |
203 | #[serde(skip)] | |
204 | __do_not_match_exhaustively: (), | |
205 | } | |
206 | ||
207 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
208 | /// Information about a dependency kind. | |
209 | pub struct DepKindInfo { | |
210 | /// The kind of dependency. | |
211 | #[serde(deserialize_with = "dependency::parse_dependency_kind")] | |
212 | pub kind: DependencyKind, | |
213 | /// The target platform for the dependency. | |
214 | /// | |
215 | /// This is `None` if it is not a target dependency. | |
216 | /// | |
217 | /// Use the [`Display`] trait to access the contents. | |
218 | /// | |
219 | /// By default all platform dependencies are included in the resolve | |
220 | /// graph. Use Cargo's `--filter-platform` flag if you only want to | |
221 | /// include dependencies for a specific platform. | |
222 | /// | |
223 | /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html | |
224 | pub target: Option<dependency::Platform>, | |
225 | #[doc(hidden)] | |
226 | #[serde(skip)] | |
227 | __do_not_match_exhaustively: (), | |
228 | } | |
229 | ||
230 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
231 | /// A crate | |
232 | pub struct Package { | |
233 | /// Name as given in the `Cargo.toml` | |
234 | pub name: String, | |
235 | /// Version given in the `Cargo.toml` | |
236 | pub version: Version, | |
237 | /// Authors given in the `Cargo.toml` | |
238 | #[serde(default)] | |
239 | pub authors: Vec<String>, | |
240 | /// An opaque identifier for a package | |
241 | pub id: PackageId, | |
242 | /// The source of the package, e.g. | |
243 | /// crates.io or `None` for local projects. | |
244 | pub source: Option<Source>, | |
245 | /// Description as given in the `Cargo.toml` | |
246 | pub description: Option<String>, | |
247 | /// List of dependencies of this particular package | |
248 | pub dependencies: Vec<Dependency>, | |
249 | /// License as given in the `Cargo.toml` | |
250 | pub license: Option<String>, | |
251 | /// If the package is using a nonstandard license, this key may be specified instead of | |
252 | /// `license`, and must point to a file relative to the manifest. | |
253 | pub license_file: Option<PathBuf>, | |
254 | /// Targets provided by the crate (lib, bin, example, test, ...) | |
255 | pub targets: Vec<Target>, | |
256 | /// Features provided by the crate, mapped to the features required by that feature. | |
257 | pub features: HashMap<String, Vec<String>>, | |
258 | /// Path containing the `Cargo.toml` | |
259 | pub manifest_path: PathBuf, | |
260 | /// Categories as given in the `Cargo.toml` | |
261 | #[serde(default)] | |
262 | pub categories: Vec<String>, | |
263 | /// Keywords as given in the `Cargo.toml` | |
264 | #[serde(default)] | |
265 | pub keywords: Vec<String>, | |
266 | /// Readme as given in the `Cargo.toml` | |
267 | pub readme: Option<PathBuf>, | |
268 | /// Repository as given in the `Cargo.toml` | |
269 | // can't use `url::Url` because that requires a more recent stable compiler | |
270 | pub repository: Option<String>, | |
271 | /// Default Rust edition for the package | |
272 | /// | |
273 | /// Beware that individual targets may specify their own edition in | |
274 | /// [`Target::edition`](struct.Target.html#structfield.edition). | |
275 | #[serde(default = "edition_default")] | |
276 | pub edition: String, | |
277 | /// Contents of the free form package.metadata section | |
278 | /// | |
279 | /// This contents can be serialized to a struct using serde: | |
280 | /// | |
281 | /// ```rust | |
3dfed10e XL |
282 | /// use serde::Deserialize; |
283 | /// use serde_json::json; | |
ba9703b0 XL |
284 | /// |
285 | /// #[derive(Debug, Deserialize)] | |
286 | /// struct SomePackageMetadata { | |
287 | /// some_value: i32, | |
288 | /// } | |
289 | /// | |
290 | /// fn main() { | |
291 | /// let value = json!({ | |
292 | /// "some_value": 42, | |
293 | /// }); | |
294 | /// | |
295 | /// let package_metadata: SomePackageMetadata = serde_json::from_value(value).unwrap(); | |
296 | /// assert_eq!(package_metadata.some_value, 42); | |
297 | /// } | |
298 | /// | |
299 | /// ``` | |
300 | #[serde(default)] | |
301 | pub metadata: serde_json::Value, | |
302 | /// The name of a native library the package is linking to. | |
303 | pub links: Option<String>, | |
304 | /// List of registries to which this package may be published. | |
305 | /// | |
306 | /// Publishing is unrestricted if `None`, and forbidden if the `Vec` is empty. | |
307 | /// | |
308 | /// This is always `None` if running with a version of Cargo older than 1.39. | |
309 | pub publish: Option<Vec<String>>, | |
310 | #[doc(hidden)] | |
311 | #[serde(skip)] | |
312 | __do_not_match_exhaustively: (), | |
313 | } | |
314 | ||
315 | impl Package { | |
316 | /// Full path to the license file if one is present in the manifest | |
317 | pub fn license_file(&self) -> Option<PathBuf> { | |
3dfed10e XL |
318 | self.license_file.as_ref().map(|file| { |
319 | self.manifest_path | |
320 | .parent() | |
321 | .unwrap_or(&self.manifest_path) | |
322 | .join(file) | |
323 | }) | |
ba9703b0 XL |
324 | } |
325 | ||
326 | /// Full path to the readme file if one is present in the manifest | |
327 | pub fn readme(&self) -> Option<PathBuf> { | |
328 | self.readme | |
329 | .as_ref() | |
330 | .map(|file| self.manifest_path.join(file)) | |
331 | } | |
332 | } | |
333 | ||
334 | /// The source of a package such as crates.io. | |
335 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
336 | #[serde(transparent)] | |
f035d41b XL |
337 | pub struct Source { |
338 | /// The underlying string representation of a source. | |
339 | pub repr: String, | |
340 | } | |
ba9703b0 XL |
341 | |
342 | impl Source { | |
343 | /// Returns true if the source is crates.io. | |
344 | pub fn is_crates_io(&self) -> bool { | |
f035d41b | 345 | self.repr == "registry+https://github.com/rust-lang/crates.io-index" |
ba9703b0 XL |
346 | } |
347 | } | |
348 | ||
349 | impl std::fmt::Display for Source { | |
350 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
f035d41b | 351 | fmt::Display::fmt(&self.repr, f) |
ba9703b0 XL |
352 | } |
353 | } | |
354 | ||
355 | #[derive(Clone, Serialize, Deserialize, Debug)] | |
356 | /// A single target (lib, bin, example, ...) provided by a crate | |
357 | pub struct Target { | |
358 | /// Name as given in the `Cargo.toml` or generated from the file name | |
359 | pub name: String, | |
360 | /// Kind of target ("bin", "example", "test", "bench", "lib") | |
361 | pub kind: Vec<String>, | |
f035d41b | 362 | /// Almost the same as `kind`, except when an example is a library instead of an executable. |
ba9703b0 XL |
363 | /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example` |
364 | #[serde(default)] | |
365 | pub crate_types: Vec<String>, | |
366 | ||
367 | #[serde(default)] | |
368 | #[serde(rename = "required-features")] | |
369 | /// This target is built only if these features are enabled. | |
370 | /// It doesn't apply to `lib` targets. | |
371 | pub required_features: Vec<String>, | |
372 | /// Path to the main source file of the target | |
373 | pub src_path: PathBuf, | |
374 | /// Rust edition for this target | |
375 | #[serde(default = "edition_default")] | |
376 | pub edition: String, | |
377 | /// Whether or not this target has doc tests enabled, and the target is | |
378 | /// compatible with doc testing. | |
379 | /// | |
380 | /// This is always `true` if running with a version of Cargo older than 1.37. | |
381 | #[serde(default = "default_true")] | |
382 | pub doctest: bool, | |
383 | #[doc(hidden)] | |
384 | #[serde(skip)] | |
385 | __do_not_match_exhaustively: (), | |
386 | } | |
387 | ||
388 | fn default_true() -> bool { | |
389 | true | |
390 | } | |
391 | ||
392 | fn edition_default() -> String { | |
393 | "2015".to_string() | |
394 | } | |
395 | ||
396 | /// Cargo features flags | |
397 | #[derive(Debug, Clone)] | |
398 | pub enum CargoOpt { | |
399 | /// Run cargo with `--features-all` | |
400 | AllFeatures, | |
401 | /// Run cargo with `--no-default-features` | |
402 | NoDefaultFeatures, | |
403 | /// Run cargo with `--features <FEATURES>` | |
404 | SomeFeatures(Vec<String>), | |
405 | } | |
406 | ||
407 | /// A builder for configurating `cargo metadata` invocation. | |
408 | #[derive(Debug, Clone, Default)] | |
409 | pub struct MetadataCommand { | |
410 | cargo_path: Option<PathBuf>, | |
411 | manifest_path: Option<PathBuf>, | |
412 | current_dir: Option<PathBuf>, | |
413 | no_deps: bool, | |
3dfed10e XL |
414 | /// Collections of `CargoOpt::SomeFeatures(..)` |
415 | features: Vec<String>, | |
416 | /// Latched `CargoOpt::AllFeatures` | |
417 | all_features: bool, | |
418 | /// Latched `CargoOpt::NoDefaultFeatures` | |
419 | no_default_features: bool, | |
ba9703b0 XL |
420 | other_options: Vec<String>, |
421 | } | |
422 | ||
423 | impl MetadataCommand { | |
424 | /// Creates a default `cargo metadata` command, which will look for | |
425 | /// `Cargo.toml` in the ancestors of the current directory. | |
426 | pub fn new() -> MetadataCommand { | |
427 | MetadataCommand::default() | |
428 | } | |
429 | /// Path to `cargo` executable. If not set, this will use the | |
430 | /// the `$CARGO` environment variable, and if that is not set, will | |
431 | /// simply be `cargo`. | |
f035d41b XL |
432 | pub fn cargo_path(&mut self, path: impl Into<PathBuf>) -> &mut MetadataCommand { |
433 | self.cargo_path = Some(path.into()); | |
ba9703b0 XL |
434 | self |
435 | } | |
436 | /// Path to `Cargo.toml` | |
f035d41b XL |
437 | pub fn manifest_path(&mut self, path: impl Into<PathBuf>) -> &mut MetadataCommand { |
438 | self.manifest_path = Some(path.into()); | |
ba9703b0 XL |
439 | self |
440 | } | |
441 | /// Current directory of the `cargo metadata` process. | |
f035d41b XL |
442 | pub fn current_dir(&mut self, path: impl Into<PathBuf>) -> &mut MetadataCommand { |
443 | self.current_dir = Some(path.into()); | |
ba9703b0 XL |
444 | self |
445 | } | |
446 | /// Output information only about the root package and don't fetch dependencies. | |
447 | pub fn no_deps(&mut self) -> &mut MetadataCommand { | |
448 | self.no_deps = true; | |
449 | self | |
450 | } | |
451 | /// Which features to include. | |
3dfed10e XL |
452 | /// |
453 | /// Call this multiple times to specify advanced feature configurations: | |
454 | /// | |
455 | /// ```no_run | |
456 | /// # use cargo_metadata::{CargoOpt, MetadataCommand}; | |
457 | /// MetadataCommand::new() | |
458 | /// .features(CargoOpt::NoDefaultFeatures) | |
459 | /// .features(CargoOpt::SomeFeatures(vec!["feat1".into(), "feat2".into()])) | |
460 | /// .features(CargoOpt::SomeFeatures(vec!["feat3".into()])) | |
461 | /// // ... | |
462 | /// # ; | |
463 | /// ``` | |
464 | /// | |
465 | /// # Panics | |
466 | /// | |
467 | /// `cargo metadata` rejects multiple `--no-default-features` flags. Similarly, the `features()` | |
468 | /// method panics when specifiying multiple `CargoOpt::NoDefaultFeatures`: | |
469 | /// | |
470 | /// ```should_panic | |
471 | /// # use cargo_metadata::{CargoOpt, MetadataCommand}; | |
472 | /// MetadataCommand::new() | |
473 | /// .features(CargoOpt::NoDefaultFeatures) | |
474 | /// .features(CargoOpt::NoDefaultFeatures) // <-- panic! | |
475 | /// // ... | |
476 | /// # ; | |
477 | /// ``` | |
478 | /// | |
479 | /// The method also panics for multiple `CargoOpt::AllFeatures` arguments: | |
480 | /// | |
481 | /// ```should_panic | |
482 | /// # use cargo_metadata::{CargoOpt, MetadataCommand}; | |
483 | /// MetadataCommand::new() | |
484 | /// .features(CargoOpt::AllFeatures) | |
485 | /// .features(CargoOpt::AllFeatures) // <-- panic! | |
486 | /// // ... | |
487 | /// # ; | |
488 | /// ``` | |
ba9703b0 | 489 | pub fn features(&mut self, features: CargoOpt) -> &mut MetadataCommand { |
3dfed10e XL |
490 | match features { |
491 | CargoOpt::SomeFeatures(features) => self.features.extend(features), | |
492 | CargoOpt::NoDefaultFeatures => { | |
493 | assert!( | |
494 | !self.no_default_features, | |
495 | "Do not supply CargoOpt::NoDefaultFeatures more than once!" | |
496 | ); | |
497 | self.no_default_features = true; | |
498 | } | |
499 | CargoOpt::AllFeatures => { | |
500 | assert!( | |
501 | !self.all_features, | |
502 | "Do not supply CargoOpt::AllFeatures more than once!" | |
503 | ); | |
504 | self.all_features = true; | |
505 | } | |
506 | } | |
ba9703b0 XL |
507 | self |
508 | } | |
509 | /// Arbitrary command line flags to pass to `cargo`. These will be added | |
510 | /// to the end of the command line invocation. | |
f035d41b XL |
511 | pub fn other_options(&mut self, options: impl Into<Vec<String>>) -> &mut MetadataCommand { |
512 | self.other_options = options.into(); | |
ba9703b0 XL |
513 | self |
514 | } | |
f035d41b XL |
515 | |
516 | /// Builds a command for `cargo metadata`. This is the first | |
517 | /// part of the work of `exec`. | |
518 | pub fn cargo_command(&self) -> Result<Command> { | |
ba9703b0 XL |
519 | let cargo = self |
520 | .cargo_path | |
521 | .clone() | |
3dfed10e | 522 | .or_else(|| env::var("CARGO").map(PathBuf::from).ok()) |
ba9703b0 XL |
523 | .unwrap_or_else(|| PathBuf::from("cargo")); |
524 | let mut cmd = Command::new(cargo); | |
525 | cmd.args(&["metadata", "--format-version", "1"]); | |
526 | ||
527 | if self.no_deps { | |
528 | cmd.arg("--no-deps"); | |
529 | } | |
530 | ||
531 | if let Some(path) = self.current_dir.as_ref() { | |
532 | cmd.current_dir(path); | |
533 | } | |
534 | ||
3dfed10e XL |
535 | if !self.features.is_empty() { |
536 | cmd.arg("--features").arg(self.features.join(",")); | |
537 | } | |
538 | if self.all_features { | |
539 | cmd.arg("--all-features"); | |
540 | } | |
541 | if self.no_default_features { | |
542 | cmd.arg("--no-default-features"); | |
ba9703b0 XL |
543 | } |
544 | ||
545 | if let Some(manifest_path) = &self.manifest_path { | |
546 | cmd.arg("--manifest-path").arg(manifest_path.as_os_str()); | |
547 | } | |
548 | cmd.args(&self.other_options); | |
f035d41b XL |
549 | |
550 | Ok(cmd) | |
551 | } | |
552 | ||
553 | /// Parses `cargo metadata` output. `data` must have been | |
554 | /// produced by a command built with `cargo_command`. | |
3dfed10e | 555 | pub fn parse<T: AsRef<str>>(data: T) -> Result<Metadata> { |
f035d41b XL |
556 | let meta = serde_json::from_str(data.as_ref())?; |
557 | Ok(meta) | |
558 | } | |
559 | ||
560 | /// Runs configured `cargo metadata` and returns parsed `Metadata`. | |
561 | pub fn exec(&self) -> Result<Metadata> { | |
562 | let mut cmd = self.cargo_command()?; | |
ba9703b0 XL |
563 | let output = cmd.output()?; |
564 | if !output.status.success() { | |
565 | return Err(Error::CargoMetadata { | |
566 | stderr: String::from_utf8(output.stderr)?, | |
567 | }); | |
568 | } | |
569 | let stdout = from_utf8(&output.stdout)? | |
570 | .lines() | |
571 | .find(|line| line.starts_with('{')) | |
572 | .ok_or_else(|| Error::NoJson)?; | |
f035d41b | 573 | Self::parse(stdout) |
ba9703b0 XL |
574 | } |
575 | } |