@@ -7,13 +7,15 @@ use cargo_lock::{
77 dependency:: graph:: EdgeDirection ,
88 dependency:: Tree ,
99 package:: { self } ,
10- Dependency , Lockfile , ResolveVersion ,
10+ Dependency , Lockfile , Package , ResolveVersion , Version ,
1111} ;
1212use gumdrop:: Options ;
13+ use petgraph:: graph:: NodeIndex ;
1314use std:: {
1415 env, fs, io,
1516 path:: { Path , PathBuf } ,
1617 process:: exit,
18+ str:: FromStr ,
1719} ;
1820
1921/// `cargo lock` subcommands
@@ -126,12 +128,66 @@ impl TranslateCmd {
126128#[ derive( Debug , Options ) ]
127129struct TreeCmd {
128130 /// Input `Cargo.lock` file
129- #[ options( short = "f" , help = "input Cargo.lock file to translate" ) ]
131+ #[ options(
132+ short = "f" ,
133+ long = "file" ,
134+ help = "input Cargo.lock file to translate"
135+ ) ]
130136 file : Option < PathBuf > ,
131137
132- /// Dependencies names to draw a tree for
133- #[ options( free, help = "dependency names to draw trees for" ) ]
134- dependencies : Vec < package:: Name > ,
138+ /// Show exact package identities (checksums or specific source versions) when available
139+ #[ options(
140+ short = "x" ,
141+ long = "exact" ,
142+ help = "show exact package identies (checksums or specific source versions) when available"
143+ ) ]
144+ exact : bool ,
145+
146+ // Show inverse dependencies rather than forward dependencies
147+ #[ options(
148+ short = "i" ,
149+ long = "invert" ,
150+ help = "show inverse dependencies _on_ a package, rather than forward dependencies _of_ a package"
151+ ) ]
152+ inverse : bool ,
153+
154+ /// Dependencies names or hashes to draw a tree for
155+ #[ options( free, help = "dependency names or hashes to draw trees for" ) ]
156+ dependencies : Vec < String > ,
157+ }
158+
159+ fn package_matches_name ( pkg : & Package , name : & str ) -> bool {
160+ pkg. name . as_str ( ) == name
161+ }
162+
163+ fn package_matches_ver ( pkg : & Package , ver : & str ) -> bool {
164+ // Try interpreting ver as a semver string.
165+ if let Ok ( v) = Version :: from_str ( ver) {
166+ return pkg. version == v;
167+ }
168+ // Try comparing ver to hashes in either the package checksum or the source
169+ // precise field
170+ if let Some ( cksum) = & pkg. checksum {
171+ if cksum. to_string ( ) == ver {
172+ return true ;
173+ }
174+ }
175+ if let Some ( src) = & pkg. source {
176+ if let Some ( precise) = src. precise ( ) {
177+ if precise == ver {
178+ return true ;
179+ }
180+ }
181+ }
182+ false
183+ }
184+
185+ fn package_matches ( pkg : & Package , spec : & str ) -> bool {
186+ if let Some ( ( name, ver) ) = spec. split_once ( '@' ) {
187+ package_matches_name ( pkg, name) && package_matches_ver ( pkg, ver)
188+ } else {
189+ package_matches_name ( pkg, spec) || package_matches_ver ( pkg, spec)
190+ }
135191}
136192
137193impl TreeCmd {
@@ -144,43 +200,40 @@ impl TreeCmd {
144200 exit ( 1 ) ;
145201 } ) ;
146202
147- if self . dependencies . is_empty ( ) {
148- self . dependency_tree ( & tree ) ;
203+ let indices : Vec < NodeIndex > = if self . dependencies . is_empty ( ) {
204+ tree . roots ( ) . to_vec ( )
149205 } else {
150- self . inverse_dependency_tree ( & lockfile, & tree) ;
151- }
152- }
153-
154- /// Show forward dependency tree for detected root dependencies
155- fn dependency_tree ( & self , tree : & Tree ) {
156- for ( i, index) in tree. roots ( ) . iter ( ) . enumerate ( ) {
157- if i > 0 {
158- println ! ( ) ;
159- }
160-
161- tree. render ( & mut io:: stdout ( ) , * index, EdgeDirection :: Outgoing )
162- . unwrap ( ) ;
163- }
206+ self . dependencies
207+ . iter ( )
208+ . map ( |dep| {
209+ let package = lockfile
210+ . packages
211+ . iter ( )
212+ . find ( |pkg| package_matches ( pkg, dep) )
213+ . unwrap_or_else ( || {
214+ eprintln ! ( "*** error: invalid dependency name: `{}`" , dep) ;
215+ exit ( 1 ) ;
216+ } ) ;
217+ tree. nodes ( ) [ & package. into ( ) ]
218+ } )
219+ . collect ( )
220+ } ;
221+
222+ self . dependency_tree ( & tree, & indices) ;
164223 }
165224
166- /// Show inverse dependency tree for the provided dependencies
167- fn inverse_dependency_tree ( & self , lockfile : & Lockfile , tree : & Tree ) {
168- for ( i, dep ) in self . dependencies . iter ( ) . enumerate ( ) {
225+ /// Show dependency tree for the provided dependencies
226+ fn dependency_tree ( & self , tree : & Tree , indices : & [ NodeIndex ] ) {
227+ for ( i, index ) in indices . iter ( ) . enumerate ( ) {
169228 if i > 0 {
170229 println ! ( ) ;
171230 }
172-
173- let package = lockfile
174- . packages
175- . iter ( )
176- . find ( |pkg| pkg. name == * dep)
177- . unwrap_or_else ( || {
178- eprintln ! ( "*** error: invalid dependency name: `{}`" , dep) ;
179- exit ( 1 ) ;
180- } ) ;
181-
182- let index = tree. nodes ( ) [ & package. into ( ) ] ;
183- tree. render ( & mut io:: stdout ( ) , index, EdgeDirection :: Incoming )
231+ let direction = if self . inverse {
232+ EdgeDirection :: Incoming
233+ } else {
234+ EdgeDirection :: Outgoing
235+ } ;
236+ tree. render ( & mut io:: stdout ( ) , * index, direction, self . exact )
184237 . unwrap ( ) ;
185238 }
186239 }
0 commit comments