1
1
use cargo:: core:: dependency:: DepKind ;
2
+ use cargo:: core:: PackageIdSpec ;
2
3
use cargo:: core:: Workspace ;
3
4
use cargo:: ops:: cargo_remove:: remove;
4
5
use cargo:: ops:: cargo_remove:: RemoveOptions ;
5
6
use cargo:: ops:: resolve_ws;
6
7
use cargo:: util:: command_prelude:: * ;
7
8
use cargo:: util:: print_available_packages;
9
+ use cargo:: util:: toml_mut:: dependency:: Dependency ;
10
+ use cargo:: util:: toml_mut:: dependency:: MaybeWorkspace ;
11
+ use cargo:: util:: toml_mut:: dependency:: Source ;
8
12
use cargo:: util:: toml_mut:: manifest:: DepTable ;
9
13
use cargo:: util:: toml_mut:: manifest:: LocalManifest ;
10
14
use cargo:: CargoResult ;
@@ -86,7 +90,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
86
90
. get_many :: < String > ( "dependencies" )
87
91
. expect ( "required(true)" )
88
92
. cloned ( )
89
- . collect ( ) ;
93
+ . collect :: < Vec < _ > > ( ) ;
90
94
91
95
let section = parse_section ( args) ;
92
96
@@ -100,8 +104,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
100
104
remove ( & options) ?;
101
105
102
106
if !dry_run {
103
- // Clean up workspace dependencies
104
- gc_workspace ( & workspace, & options . dependencies ) ?;
107
+ // Clean up the workspace
108
+ gc_workspace ( & workspace) ?;
105
109
106
110
// Reload the workspace since we've changed dependencies
107
111
let ws = args. workspace ( config) ?;
@@ -133,8 +137,9 @@ fn parse_section(args: &ArgMatches) -> DepTable {
133
137
table
134
138
}
135
139
136
- /// Clean up workspace dependencies which no longer have a reference to them.
137
- fn gc_workspace ( workspace : & Workspace < ' _ > , dependencies : & [ String ] ) -> CargoResult < ( ) > {
140
+ /// Clean up the workspace.dependencies, profile, patch, and replace sections of the root manifest
141
+ /// by removing dependencies which no longer have a reference to them.
142
+ fn gc_workspace ( workspace : & Workspace < ' _ > ) -> CargoResult < ( ) > {
138
143
let mut manifest: toml_edit:: Document =
139
144
cargo_util:: paths:: read ( workspace. root_manifest ( ) ) ?. parse ( ) ?;
140
145
@@ -143,9 +148,104 @@ fn gc_workspace(workspace: &Workspace<'_>, dependencies: &[String]) -> CargoResu
143
148
. map ( |p| LocalManifest :: try_new ( p. manifest_path ( ) ) )
144
149
. collect :: < CargoResult < Vec < _ > > > ( ) ?;
145
150
146
- for dep in dependencies {
147
- if !dep_in_workspace ( dep, & members) {
148
- remove_workspace_dep ( dep, & mut manifest) ;
151
+ let dependencies = members
152
+ . iter ( )
153
+ . flat_map ( |manifest| {
154
+ manifest. get_sections ( ) . into_iter ( ) . flat_map ( |( _, table) | {
155
+ table
156
+ . as_table_like ( )
157
+ . unwrap ( )
158
+ . iter ( )
159
+ . map ( |( key, item) | Dependency :: from_toml ( & manifest. path , key, item) )
160
+ . collect :: < Vec < _ > > ( )
161
+ } )
162
+ } )
163
+ . collect :: < CargoResult < Vec < _ > > > ( ) ?;
164
+
165
+ // Clean up the workspace.dependencies section
166
+ if let Some ( toml_edit:: Item :: Table ( deps_table) ) = manifest
167
+ . get_mut ( "workspace" )
168
+ . and_then ( |t| t. get_mut ( "dependencies" ) )
169
+ {
170
+ deps_table. set_implicit ( true ) ;
171
+ for ( key, item) in deps_table. iter_mut ( ) {
172
+ if !dependencies. iter ( ) . any ( |d| {
173
+ d. toml_key ( ) == key. get ( ) && matches ! ( d. source( ) , Some ( Source :: Workspace ( _) ) )
174
+ } ) {
175
+ * item = toml_edit:: Item :: None ;
176
+ }
177
+ }
178
+ }
179
+
180
+ // Clean up the profile section
181
+ //
182
+ // Example tables:
183
+ // - profile.dev.package.foo
184
+ // - profile.release.package."*"
185
+ // - profile.release.package."foo:2.1.0"
186
+ if let Some ( toml_edit:: Item :: Table ( profile_section_table) ) = manifest. get_mut ( "profile" ) {
187
+ profile_section_table. set_implicit ( true ) ;
188
+
189
+ for ( _, item) in profile_section_table. iter_mut ( ) {
190
+ if let toml_edit:: Item :: Table ( profile_table) = item {
191
+ profile_table. set_implicit ( true ) ;
192
+
193
+ if let Some ( toml_edit:: Item :: Table ( package_table) ) =
194
+ profile_table. get_mut ( "package" )
195
+ {
196
+ package_table. set_implicit ( true ) ;
197
+
198
+ for ( key, item) in package_table. iter_mut ( ) {
199
+ if !spec_has_match (
200
+ & PackageIdSpec :: parse ( key. get ( ) ) ?,
201
+ & dependencies,
202
+ workspace. config ( ) ,
203
+ ) ? {
204
+ * item = toml_edit:: Item :: None ;
205
+ }
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ // Clean up the patch section
213
+ if let Some ( toml_edit:: Item :: Table ( patch_section_table) ) = manifest. get_mut ( "patch" ) {
214
+ patch_section_table. set_implicit ( true ) ;
215
+
216
+ // The key in each of the subtables is a source (either a registry or a URL)
217
+ for ( source, item) in patch_section_table. iter_mut ( ) {
218
+ if let toml_edit:: Item :: Table ( patch_table) = item {
219
+ patch_table. set_implicit ( true ) ;
220
+
221
+ for ( key, item) in patch_table. iter_mut ( ) {
222
+ let package_name =
223
+ Dependency :: from_toml ( & workspace. root_manifest ( ) , key. get ( ) , item) ?. name ;
224
+ if !source_has_match (
225
+ & package_name,
226
+ source. get ( ) ,
227
+ & dependencies,
228
+ workspace. config ( ) ,
229
+ ) ? {
230
+ * item = toml_edit:: Item :: None ;
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ // Clean up the replace section
238
+ if let Some ( toml_edit:: Item :: Table ( table) ) = manifest. get_mut ( "replace" ) {
239
+ table. set_implicit ( true ) ;
240
+
241
+ for ( key, item) in table. iter_mut ( ) {
242
+ if !spec_has_match (
243
+ & PackageIdSpec :: parse ( key. get ( ) ) ?,
244
+ & dependencies,
245
+ workspace. config ( ) ,
246
+ ) ? {
247
+ * item = toml_edit:: Item :: None ;
248
+ }
149
249
}
150
250
}
151
251
@@ -154,28 +254,72 @@ fn gc_workspace(workspace: &Workspace<'_>, dependencies: &[String]) -> CargoResu
154
254
Ok ( ( ) )
155
255
}
156
256
157
- /// Get whether or not a dependency is depended upon in a workspace.
158
- fn dep_in_workspace ( dep : & str , members : & [ LocalManifest ] ) -> bool {
159
- members. iter ( ) . any ( |manifest| {
160
- manifest. get_sections ( ) . iter ( ) . any ( |( _, table) | {
161
- table
162
- . as_table_like ( )
163
- . unwrap ( )
164
- . get ( dep)
165
- . and_then ( |t| t. get ( "workspace" ) )
166
- . and_then ( |v| v. as_bool ( ) )
167
- . unwrap_or ( false )
168
- } )
169
- } )
257
+ /// Check whether or not a package ID spec matches any dependencies.
258
+ fn spec_has_match (
259
+ spec : & PackageIdSpec ,
260
+ dependencies : & [ Dependency ] ,
261
+ config : & Config ,
262
+ ) -> CargoResult < bool > {
263
+ for dep in dependencies {
264
+ match dep. source_id ( config) ? {
265
+ MaybeWorkspace :: Other ( source_id) => {
266
+ if spec. name ( ) . as_str ( ) != & dep. name {
267
+ continue ;
268
+ }
269
+
270
+ let version_matches = match ( spec. version ( ) , dep. version ( ) ) {
271
+ ( Some ( v) , Some ( vq) ) => semver:: VersionReq :: parse ( vq) ?. matches ( v) ,
272
+ ( Some ( _) , None ) => false ,
273
+ ( None , _) => true ,
274
+ } ;
275
+ if !version_matches {
276
+ continue ;
277
+ }
278
+
279
+ let source_matches = match spec. url ( ) {
280
+ Some ( u) => u == source_id. url ( ) ,
281
+ None => true ,
282
+ } ;
283
+ if source_matches {
284
+ return Ok ( true ) ;
285
+ }
286
+ }
287
+ MaybeWorkspace :: Workspace ( _) => { }
288
+ }
289
+ }
290
+
291
+ Ok ( false )
170
292
}
171
293
172
- /// Remove a dependency from a workspace manifest.
173
- fn remove_workspace_dep ( dep : & str , ws_manifest : & mut toml_edit:: Document ) {
174
- if let Some ( toml_edit:: Item :: Table ( table) ) = ws_manifest
175
- . get_mut ( "workspace" )
176
- . and_then ( |t| t. get_mut ( "dependencies" ) )
177
- {
178
- table. set_implicit ( true ) ;
179
- table. remove ( dep) ;
294
+ /// Check whether or not a source (URL or registry name) matches any dependencies.
295
+ fn source_has_match (
296
+ name : & str ,
297
+ source : & str ,
298
+ dependencies : & [ Dependency ] ,
299
+ config : & Config ,
300
+ ) -> CargoResult < bool > {
301
+ for dep in dependencies {
302
+ if & dep. name != name {
303
+ continue ;
304
+ }
305
+
306
+ match dep. source_id ( config) ? {
307
+ MaybeWorkspace :: Other ( source_id) => {
308
+ if source_id. is_registry ( ) {
309
+ if source_id. display_registry_name ( ) == source
310
+ || source_id. url ( ) . as_str ( ) == source
311
+ {
312
+ return Ok ( true ) ;
313
+ }
314
+ } else if source_id. is_git ( ) {
315
+ if source_id. url ( ) . as_str ( ) == source {
316
+ return Ok ( true ) ;
317
+ }
318
+ }
319
+ }
320
+ MaybeWorkspace :: Workspace ( _) => { }
321
+ }
180
322
}
323
+
324
+ Ok ( false )
181
325
}
0 commit comments