11use anyhow:: bail;
22use cargo_metadata:: {
3- semver :: VersionReq , CargoOpt :: AllFeatures , CargoOpt :: NoDefaultFeatures , Dependency ,
4- DependencyKind , Metadata , MetadataCommand , Package , PackageId ,
3+ CargoOpt :: AllFeatures , CargoOpt :: NoDefaultFeatures , DependencyKind , Metadata , MetadataCommand ,
4+ NodeDep , Package , PackageId ,
55} ;
66use std:: collections:: { HashMap , HashSet } ;
77
@@ -85,12 +85,12 @@ fn sourced_dependencies_from_metadata(
8585 }
8686 }
8787
88- for pkg in meta. workspace_members {
89- * how. get_mut ( & pkg) . unwrap ( ) = PkgSource :: Local ;
88+ for pkg in & meta. workspace_members {
89+ * how. get_mut ( pkg) . unwrap ( ) = PkgSource :: Local ;
9090 }
9191
9292 if no_dev {
93- ( how, what) = extract_non_dev_dependencies ( & mut how, & mut what) ;
93+ ( how, what) = extract_non_dev_dependencies ( & meta , & mut how, & mut what) ;
9494 }
9595
9696 let dependencies: Vec < _ > = how
@@ -107,42 +107,30 @@ fn sourced_dependencies_from_metadata(
107107 Ok ( dependencies)
108108}
109109
110- #[ derive( Eq , Hash , PartialEq ) ]
111- struct Dep {
112- name : String ,
113- req : VersionReq ,
114- }
115-
116- impl Dep {
117- fn from_cargo_metadata_dependency ( dep : & Dependency ) -> Self {
118- Self {
119- name : dep. name . clone ( ) ,
120- req : dep. req . clone ( ) ,
121- }
122- }
123-
124- fn matches ( & self , pkg : & Package ) -> bool {
125- self . name == pkg. name && self . req . matches ( & pkg. version )
126- }
127- }
128-
129- /// Start with the `PkgSource::Local` packages, then iteratively add non-dev-dependencies until no more
130- /// packages can be added, and return the results.
131- ///
132- /// Note that matching dependencies to packages is "best effort." The fields that Cargo uses to
133- /// determine a package's id are its name, version, and source:
134- /// https://github.com/rust-lang/cargo/blob/dd5134c7a59e3a3b8587f1ef04a930185d2ca503/src/cargo/core/package_id.rs#L29-L31
110+ /// Start with the `PkgSource::Local` packages, then iteratively add non-dev-dependencies until no
111+ /// more packages can be added, and return the results.
135112///
136- /// When matching dependencies to packages, we use the package's name and version, but not its source
137- /// (see [`Dep`]). Experiments suggest that source strings can vary. So comparing them seems risky.
138- /// Also, it is better to err on the side of inclusion .
113+ /// This function uses the resolved dependency graph from `cargo metadata` to determine which
114+ /// dependencies are actually used. This function does _not_ use the declared dependencies, which
115+ /// may include optional dependencies that aren't actually used .
139116fn extract_non_dev_dependencies (
117+ meta : & Metadata ,
140118 how : & mut HashMap < PackageId , PkgSource > ,
141119 what : & mut HashMap < PackageId , Package > ,
142120) -> ( HashMap < PackageId , PkgSource > , HashMap < PackageId , Package > ) {
143121 let mut how_new = HashMap :: new ( ) ;
144122 let mut what_new = HashMap :: new ( ) ;
145123
124+ let Some ( resolve) = & meta. resolve else {
125+ return ( HashMap :: new ( ) , HashMap :: new ( ) ) ;
126+ } ;
127+
128+ let node_deps: HashMap < & PackageId , & [ NodeDep ] > = resolve
129+ . nodes
130+ . iter ( )
131+ . map ( |node| ( & node. id , node. deps . as_slice ( ) ) )
132+ . collect ( ) ;
133+
146134 let mut ids = how
147135 . iter ( )
148136 . filter_map ( |( id, source) | {
@@ -158,19 +146,25 @@ fn extract_non_dev_dependencies(
158146 let mut deps = HashSet :: new ( ) ;
159147
160148 for id in ids. drain ( ..) {
161- for dep in & what. get ( & id) . unwrap ( ) . dependencies {
162- if dep. kind != DependencyKind :: Development {
163- deps. insert ( Dep :: from_cargo_metadata_dependency ( dep) ) ;
149+ if let Some ( node_deps) = node_deps. get ( & id) {
150+ for dep in * node_deps {
151+ if dep
152+ . dep_kinds
153+ . iter ( )
154+ . any ( |info| info. kind != DependencyKind :: Development )
155+ {
156+ deps. insert ( & dep. pkg ) ;
157+ }
164158 }
165159 }
166160
167161 how_new. insert ( id. clone ( ) , how. remove ( & id) . unwrap ( ) ) ;
168162 what_new. insert ( id. clone ( ) , what. remove ( & id) . unwrap ( ) ) ;
169163 }
170164
171- for pkg in what. values ( ) {
172- if deps. iter ( ) . any ( |dep| dep . matches ( pkg ) ) {
173- ids. push ( pkg . id . clone ( ) ) ;
165+ for pkg_id in what. keys ( ) {
166+ if deps. contains ( pkg_id ) {
167+ ids. push ( pkg_id . clone ( ) ) ;
174168 }
175169 }
176170 }
0 commit comments