]>
Commit | Line | Data |
---|---|---|
abe05a73 XL |
1 | #![deny(missing_docs)] |
2 | //! Structured access to the output of `cargo metadata` | |
3 | //! Usually used from within a `cargo-*` executable | |
4 | //! | |
5 | //! ```rust | |
6 | //! # extern crate cargo_metadata; | |
7 | //! let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path=")); | |
8 | //! let metadata = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)).unwrap(); | |
9 | //! ``` | |
10 | ||
11 | extern crate serde; | |
12 | extern crate serde_json; | |
13 | #[macro_use] extern crate serde_derive; | |
14 | ||
15 | use std::collections::HashMap; | |
16 | use std::env; | |
17 | use std::process::Command; | |
18 | use std::str::{from_utf8, Utf8Error}; | |
19 | use std::io; | |
20 | ||
21 | #[derive(Clone, Deserialize, Debug)] | |
22 | /// Starting point for metadata returned by `cargo metadata` | |
23 | pub struct Metadata { | |
24 | /// A list of all crates referenced by this crate (and the crate itself) | |
25 | pub packages: Vec<Package>, | |
26 | /// A list of all workspace members | |
27 | #[serde(default)] | |
28 | pub workspace_members: Vec<String>, | |
29 | /// Dependencies graph | |
30 | pub resolve: Option<Resolve>, | |
31 | version: usize, | |
32 | } | |
33 | ||
34 | #[derive(Clone, Deserialize, Debug)] | |
35 | /// A dependency graph | |
36 | pub struct Resolve { | |
37 | /// Nodes in a dependencies graph | |
38 | pub nodes: Vec<Node>, | |
39 | } | |
40 | ||
41 | #[derive(Clone, Deserialize, Debug)] | |
42 | /// A node in a dependencies graph | |
43 | pub struct Node { | |
44 | /// An opaque identifier for a package | |
45 | pub id: String, | |
46 | /// List of opaque identifiers for this node's dependencies | |
47 | pub dependencies: Vec<String>, | |
48 | } | |
49 | ||
50 | #[derive(Clone, Deserialize, Debug)] | |
51 | /// A crate | |
52 | pub struct Package { | |
53 | /// Name as given in the `Cargo.toml` | |
54 | pub name: String, | |
55 | /// Version given in the `Cargo.toml` | |
56 | pub version: String, | |
57 | /// An opaque identifier for a package | |
58 | pub id: String, | |
59 | source: Option<String>, | |
60 | /// List of dependencies of this particular package | |
61 | pub dependencies: Vec<Dependency>, | |
62 | /// Targets provided by the crate (lib, bin, example, test, ...) | |
63 | pub targets: Vec<Target>, | |
64 | features: HashMap<String, Vec<String>>, | |
65 | /// Path containing the `Cargo.toml` | |
66 | pub manifest_path: String, | |
67 | } | |
68 | ||
69 | #[derive(Clone, Deserialize, Debug)] | |
70 | /// A dependency of the main crate | |
71 | pub struct Dependency { | |
72 | /// Name as given in the `Cargo.toml` | |
73 | pub name: String, | |
74 | source: Option<String>, | |
75 | /// Whether this is required or optional | |
76 | pub req: String, | |
77 | kind: Option<String>, | |
78 | optional: bool, | |
79 | uses_default_features: bool, | |
80 | features: Vec<String>, | |
81 | target: Option<String>, | |
82 | } | |
83 | ||
84 | #[derive(Clone, Deserialize, Debug)] | |
85 | /// A single target (lib, bin, example, ...) provided by a crate | |
86 | pub struct Target { | |
87 | /// Name as given in the `Cargo.toml` or generated from the file name | |
88 | pub name: String, | |
89 | /// Kind of target ("bin", "example", "test", "bench", "lib") | |
90 | pub kind: Vec<String>, | |
91 | /// Almost the same as `kind`, except when an example is a library instad of an executable. | |
92 | /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example` | |
93 | #[serde(default)] | |
94 | pub crate_types: Vec<String>, | |
95 | /// Path to the main source file of the target | |
96 | pub src_path: String, | |
97 | } | |
98 | ||
99 | #[derive(Debug)] | |
100 | /// Possible errors that can occur during metadata parsing. | |
101 | pub enum Error { | |
102 | /// Error during execution of `cargo metadata` | |
103 | Io(io::Error), | |
104 | /// Output of `cargo metadata` was not valid utf8 | |
105 | Utf8(Utf8Error), | |
106 | /// Deserialization error (structure of json did not match expected structure) | |
107 | Json(serde_json::Error), | |
108 | } | |
109 | ||
110 | impl From<io::Error> for Error { | |
111 | fn from(err: io::Error) -> Self { | |
112 | Error::Io(err) | |
113 | } | |
114 | } | |
115 | impl From<Utf8Error> for Error { | |
116 | fn from(err: Utf8Error) -> Self { | |
117 | Error::Utf8(err) | |
118 | } | |
119 | } | |
120 | impl From<serde_json::Error> for Error { | |
121 | fn from(err: serde_json::Error) -> Self { | |
122 | Error::Json(err) | |
123 | } | |
124 | } | |
125 | ||
126 | /// Obtain metadata only about the root package and don't fetch dependencies | |
127 | /// | |
128 | /// # Parameters | |
129 | /// | |
130 | /// - `manifest_path_arg`: Path to the manifest. | |
131 | pub fn metadata(manifest_path_arg: Option<&str>) -> Result<Metadata, Error> { | |
132 | metadata_deps(manifest_path_arg, false) | |
133 | } | |
134 | ||
135 | /// The main entry point to obtaining metadata | |
136 | /// | |
137 | /// # Parameters | |
138 | /// | |
139 | /// - `manifest_path_arg`: Path to the manifest. | |
140 | /// - `deps`: Whether to include dependencies. | |
141 | pub fn metadata_deps(manifest_path_arg: Option<&str>, deps: bool) -> Result<Metadata, Error> { | |
142 | let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); | |
143 | let mut cmd = Command::new(cargo); | |
144 | cmd.arg("metadata"); | |
145 | ||
146 | if !deps { | |
147 | cmd.arg("--no-deps"); | |
148 | } | |
149 | ||
150 | cmd.arg("--format-version").arg("1"); | |
151 | if let Some(mani) = manifest_path_arg { | |
152 | cmd.arg(mani); | |
153 | } | |
154 | let output = cmd.output()?; | |
155 | let stdout = from_utf8(&output.stdout)?; | |
156 | let meta: Metadata = serde_json::from_str(stdout)?; | |
157 | Ok(meta) | |
158 | } |