@@ -8,11 +8,7 @@ use validation::RawVersionInfo;
8
8
9
9
use serde:: { Deserialize , Serialize } ;
10
10
11
- #[ cfg( feature = "from_metadata" ) ]
12
- use std:: convert:: TryFrom ;
13
11
use std:: str:: FromStr ;
14
- #[ cfg( feature = "from_metadata" ) ]
15
- use std:: { cmp:: min, cmp:: Ordering :: * , collections:: HashMap , error:: Error , fmt:: Display } ;
16
12
17
13
/// Dependency tree embedded in the binary.
18
14
///
@@ -35,19 +31,6 @@ use std::{cmp::min, cmp::Ordering::*, collections::HashMap, error::Error, fmt::D
35
31
///
36
32
/// If deserialization succeeds, it is guaranteed that there is only one root package,
37
33
/// and that are no cyclic dependencies.
38
- ///
39
- /// ## Optional features
40
- ///
41
- /// If the `from_metadata` feature is enabled, a conversion from
42
- /// [`cargo_metadata::Metadata`](https://docs.rs/cargo_metadata/0.11.1/cargo_metadata/struct.Metadata.html)
43
- /// is possible via the `TryFrom` trait. This is the preferred way to construct this structure.
44
- /// An example demonstrating that can be found
45
- /// [here](https://github.com/rust-secure-code/cargo-auditable/blob/master/auditable-serde/examples/from-metadata.rs).
46
- ///
47
- /// If the `toml` feature is enabled, a conversion into the [`cargo_lock::Lockfile`](https://docs.rs/cargo-lock/)
48
- /// struct is possible via the `TryFrom` trait. This can be useful if you need to interoperate with tooling
49
- /// that consumes the `Cargo.lock` file format. An example demonstrating it can be found
50
- /// [here](https://github.com/rust-secure-code/cargo-auditable/blob/master/auditable-serde/examples/json-to-toml.rs).
51
34
#[ derive( Serialize , Deserialize , Debug , PartialEq , Eq , PartialOrd , Ord , Clone ) ]
52
35
#[ serde( try_from = "RawVersionInfo" ) ]
53
36
#[ cfg_attr( feature = "schema" , derive( schemars:: JsonSchema ) ) ]
@@ -123,21 +106,6 @@ impl From<Source> for String {
123
106
}
124
107
}
125
108
126
- #[ cfg( feature = "from_metadata" ) ]
127
- impl From < & cargo_metadata:: Source > for Source {
128
- fn from ( meta_source : & cargo_metadata:: Source ) -> Self {
129
- match meta_source. repr . as_str ( ) {
130
- "registry+https://github.com/rust-lang/crates.io-index" => Source :: CratesIo ,
131
- source => Source :: from (
132
- source
133
- . split ( '+' )
134
- . next ( )
135
- . expect ( "Encoding of source strings in `cargo metadata` has changed!" ) ,
136
- ) ,
137
- }
138
- }
139
- }
140
-
141
109
#[ derive( Serialize , Deserialize , Debug , PartialEq , Eq , PartialOrd , Ord , Copy , Clone , Default ) ]
142
110
#[ cfg_attr( feature = "schema" , derive( schemars:: JsonSchema ) ) ]
143
111
pub enum DependencyKind {
@@ -149,28 +117,6 @@ pub enum DependencyKind {
149
117
Runtime ,
150
118
}
151
119
152
- /// The values are ordered from weakest to strongest so that casting to integer would make sense
153
- #[ cfg( feature = "from_metadata" ) ]
154
- #[ derive( Debug , PartialEq , Eq , PartialOrd , Ord , Copy , Clone ) ]
155
- enum PrivateDepKind {
156
- Development ,
157
- Build ,
158
- Runtime ,
159
- }
160
-
161
- #[ cfg( feature = "from_metadata" ) ]
162
- impl From < PrivateDepKind > for DependencyKind {
163
- fn from ( priv_kind : PrivateDepKind ) -> Self {
164
- match priv_kind {
165
- PrivateDepKind :: Development => {
166
- panic ! ( "Cannot convert development dependency to serializable format" )
167
- }
168
- PrivateDepKind :: Build => DependencyKind :: Build ,
169
- PrivateDepKind :: Runtime => DependencyKind :: Runtime ,
170
- }
171
- }
172
- }
173
-
174
120
fn is_default < T : Default + PartialEq > ( value : & T ) -> bool {
175
121
let default_value = T :: default ( ) ;
176
122
value == & default_value
@@ -183,191 +129,6 @@ impl FromStr for VersionInfo {
183
129
}
184
130
}
185
131
186
- #[ cfg( feature = "from_metadata" ) ]
187
- impl From < & cargo_metadata:: DependencyKind > for PrivateDepKind {
188
- fn from ( kind : & cargo_metadata:: DependencyKind ) -> Self {
189
- match kind {
190
- cargo_metadata:: DependencyKind :: Normal => PrivateDepKind :: Runtime ,
191
- cargo_metadata:: DependencyKind :: Development => PrivateDepKind :: Development ,
192
- cargo_metadata:: DependencyKind :: Build => PrivateDepKind :: Build ,
193
- _ => panic ! ( "Unknown dependency kind" ) ,
194
- }
195
- }
196
- }
197
-
198
- /// Error returned by the conversion from
199
- /// [`cargo_metadata::Metadata`](https://docs.rs/cargo_metadata/0.11.1/cargo_metadata/struct.Metadata.html)
200
- #[ cfg( feature = "from_metadata" ) ]
201
- #[ derive( Debug , Copy , Clone , Eq , PartialEq ) ]
202
- pub enum InsufficientMetadata {
203
- NoDeps ,
204
- VirtualWorkspace ,
205
- }
206
-
207
- #[ cfg( feature = "from_metadata" ) ]
208
- impl Display for InsufficientMetadata {
209
- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
210
- match self {
211
- InsufficientMetadata :: NoDeps => {
212
- write ! ( f, "Missing dependency information! Please call 'cargo metadata' without '--no-deps' flag." )
213
- }
214
- InsufficientMetadata :: VirtualWorkspace => {
215
- write ! ( f, "Missing root crate! Please call this from a package directory, not workspace root." )
216
- }
217
- }
218
- }
219
- }
220
-
221
- #[ cfg( feature = "from_metadata" ) ]
222
- impl Error for InsufficientMetadata { }
223
-
224
- #[ cfg( feature = "from_metadata" ) ]
225
- impl TryFrom < & cargo_metadata:: Metadata > for VersionInfo {
226
- type Error = InsufficientMetadata ;
227
- fn try_from ( metadata : & cargo_metadata:: Metadata ) -> Result < Self , Self :: Error > {
228
- let toplevel_crate_id = metadata
229
- . resolve
230
- . as_ref ( )
231
- . ok_or ( InsufficientMetadata :: NoDeps ) ?
232
- . root
233
- . as_ref ( )
234
- . ok_or ( InsufficientMetadata :: VirtualWorkspace ) ?
235
- . repr
236
- . as_str ( ) ;
237
-
238
- // Walk the dependency tree and resolve dependency kinds for each package.
239
- // We need this because there may be several different paths to the same package
240
- // and we need to aggregate dependency types across all of them.
241
- // Moreover, `cargo metadata` doesn't propagate dependency information:
242
- // A runtime dependency of a build dependency of your package should be recorded
243
- // as *build* dependency, but Cargo flags it as a runtime dependency.
244
- // Hoo boy, here I go hand-rolling BFS again!
245
- let nodes = & metadata. resolve . as_ref ( ) . unwrap ( ) . nodes ;
246
- let id_to_node: HashMap < & str , & cargo_metadata:: Node > =
247
- nodes. iter ( ) . map ( |n| ( n. id . repr . as_str ( ) , n) ) . collect ( ) ;
248
- let mut id_to_dep_kind: HashMap < & str , PrivateDepKind > = HashMap :: new ( ) ;
249
- id_to_dep_kind. insert ( toplevel_crate_id, PrivateDepKind :: Runtime ) ;
250
- let mut current_queue: Vec < & cargo_metadata:: Node > = vec ! [ id_to_node[ toplevel_crate_id] ] ;
251
- let mut next_step_queue: Vec < & cargo_metadata:: Node > = Vec :: new ( ) ;
252
- while !current_queue. is_empty ( ) {
253
- for parent in current_queue. drain ( ..) {
254
- let parent_dep_kind = id_to_dep_kind[ parent. id . repr . as_str ( ) ] ;
255
- for child in & parent. deps {
256
- let child_id = child. pkg . repr . as_str ( ) ;
257
- let dep_kind = strongest_dep_kind ( child. dep_kinds . as_slice ( ) ) ;
258
- let dep_kind = min ( dep_kind, parent_dep_kind) ;
259
- let dep_kind_on_previous_visit = id_to_dep_kind. get ( child_id) ;
260
- if dep_kind_on_previous_visit. is_none ( )
261
- || & dep_kind > dep_kind_on_previous_visit. unwrap ( )
262
- {
263
- // if we haven't visited this node in dependency graph yet
264
- // or if we've visited it with a weaker dependency type,
265
- // records its new dependency type and add it to the queue to visit its dependencies
266
- id_to_dep_kind. insert ( child_id, dep_kind) ;
267
- next_step_queue. push ( id_to_node[ child_id] ) ;
268
- }
269
- }
270
- }
271
- std:: mem:: swap ( & mut next_step_queue, & mut current_queue) ;
272
- }
273
-
274
- let metadata_package_dep_kind = |p : & cargo_metadata:: Package | {
275
- let package_id = p. id . repr . as_str ( ) ;
276
- id_to_dep_kind. get ( package_id)
277
- } ;
278
-
279
- // Remove dev-only dependencies from the package list and collect them to Vec
280
- let mut packages: Vec < & cargo_metadata:: Package > = metadata
281
- . packages
282
- . iter ( )
283
- . filter ( |p| {
284
- let dep_kind = metadata_package_dep_kind ( p) ;
285
- // Dependencies that are present in the workspace but not used by the current root crate
286
- // will not be in the map we've built by traversing the root crate's dependencies.
287
- // In this case they will not be in the map at all. We skip them, along with dev-dependencies.
288
- dep_kind. is_some ( ) && dep_kind. unwrap ( ) != & PrivateDepKind :: Development
289
- } )
290
- . collect ( ) ;
291
-
292
- // This function is the simplest place to introduce sorting, since
293
- // it contains enough data to distinguish between equal-looking packages
294
- // and provide a stable sorting that might not be possible
295
- // using the data from VersionInfo struct alone.
296
- //
297
- // We use sort_unstable here because there is no point in
298
- // not reordering equal elements, since they're supplied by
299
- // in arbitrary order by cargo-metadata anyway
300
- // and the order even varies between executions.
301
- packages. sort_unstable_by ( |a, b| {
302
- // This is a workaround for Package not implementing Ord.
303
- // Deriving it in cargo_metadata might be more reliable?
304
- let names_order = a. name . cmp ( & b. name ) ;
305
- if names_order != Equal {
306
- return names_order;
307
- }
308
- let versions_order = a. name . cmp ( & b. name ) ;
309
- if versions_order != Equal {
310
- return versions_order;
311
- }
312
- // IDs are unique so comparing them should be sufficient
313
- a. id . repr . cmp ( & b. id . repr )
314
- } ) ;
315
-
316
- // Build a mapping from package ID to the index of that package in the Vec
317
- // because serializable representation doesn't store IDs
318
- let mut id_to_index = HashMap :: new ( ) ;
319
- for ( index, package) in packages. iter ( ) . enumerate ( ) {
320
- id_to_index. insert ( package. id . repr . as_str ( ) , index) ;
321
- }
322
-
323
- // Convert packages from cargo-metadata representation to our representation
324
- let mut packages: Vec < Package > = packages
325
- . into_iter ( )
326
- . map ( |p| Package {
327
- name : p. name . to_owned ( ) ,
328
- version : p. version . clone ( ) ,
329
- source : p. source . as_ref ( ) . map_or ( Source :: Local , Source :: from) ,
330
- kind : ( * metadata_package_dep_kind ( p) . unwrap ( ) ) . into ( ) ,
331
- dependencies : Vec :: new ( ) ,
332
- root : p. id . repr == toplevel_crate_id,
333
- } )
334
- . collect ( ) ;
335
-
336
- // Fill in dependency info from resolved dependency graph
337
- for node in metadata. resolve . as_ref ( ) . unwrap ( ) . nodes . iter ( ) {
338
- let package_id = node. id . repr . as_str ( ) ;
339
- if id_to_index. contains_key ( package_id) {
340
- // dev-dependencies are not included
341
- let package: & mut Package = & mut packages[ id_to_index[ package_id] ] ;
342
- // Dependencies
343
- for dep in node. deps . iter ( ) {
344
- // Omit the graph edge if this is a development dependency
345
- // to fix https://github.com/rustsec/rustsec/issues/1043
346
- // It is possible that something that we depend on normally
347
- // is also a dev-dependency for something,
348
- // and dev-dependencies are allowed to have cycles,
349
- // so we may end up encoding cyclic graph if we don't handle that.
350
- let dep_id = dep. pkg . repr . as_str ( ) ;
351
- if strongest_dep_kind ( & dep. dep_kinds ) != PrivateDepKind :: Development {
352
- package. dependencies . push ( id_to_index[ dep_id] ) ;
353
- }
354
- }
355
- // .sort_unstable() is fine because they're all integers
356
- package. dependencies . sort_unstable ( ) ;
357
- }
358
- }
359
- Ok ( VersionInfo { packages } )
360
- }
361
- }
362
-
363
- #[ cfg( feature = "from_metadata" ) ]
364
- fn strongest_dep_kind ( deps : & [ cargo_metadata:: DepKindInfo ] ) -> PrivateDepKind {
365
- deps. iter ( )
366
- . map ( |d| PrivateDepKind :: from ( & d. kind ) )
367
- . max ( )
368
- . unwrap_or ( PrivateDepKind :: Runtime ) // for compatibility with Rust earlier than 1.41
369
- }
370
-
371
132
#[ cfg( test) ]
372
133
mod tests {
373
134
#![ allow( unused_imports) ] // otherwise conditional compilation emits warnings
@@ -378,24 +139,6 @@ mod tests {
378
139
path:: { Path , PathBuf } ,
379
140
} ;
380
141
381
- #[ cfg( feature = "from_metadata" ) ]
382
- fn load_metadata ( cargo_toml_path : & Path ) -> cargo_metadata:: Metadata {
383
- let mut cmd = cargo_metadata:: MetadataCommand :: new ( ) ;
384
- cmd. manifest_path ( cargo_toml_path) ;
385
- cmd. exec ( ) . unwrap ( )
386
- }
387
-
388
- #[ test]
389
- #[ cfg( feature = "from_metadata" ) ]
390
- fn dependency_cycle ( ) {
391
- let cargo_toml_path = PathBuf :: from ( std:: env:: var ( "CARGO_MANIFEST_DIR" ) . unwrap ( ) )
392
- . join ( "tests/fixtures/cargo-audit-dep-cycle/Cargo.toml" ) ;
393
- let metadata = load_metadata ( & cargo_toml_path) ;
394
- let version_info_struct: VersionInfo = ( & metadata) . try_into ( ) . unwrap ( ) ;
395
- let json = serde_json:: to_string ( & version_info_struct) . unwrap ( ) ;
396
- VersionInfo :: from_str ( & json) . unwrap ( ) ; // <- the part we care about succeeding
397
- }
398
-
399
142
#[ cfg( feature = "schema" ) ]
400
143
/// Generate a JsonSchema for VersionInfo
401
144
fn generate_schema ( ) -> schemars:: schema:: RootSchema {
0 commit comments