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,49 +137,203 @@ 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 ( ) ?;
145
+ let mut is_modified = true ;
140
146
141
147
let members = workspace
142
148
. members ( )
143
149
. map ( |p| LocalManifest :: try_new ( p. manifest_path ( ) ) )
144
150
. collect :: < CargoResult < Vec < _ > > > ( ) ?;
145
151
146
- for dep in dependencies {
147
- if !dep_in_workspace ( dep, & members) {
148
- remove_workspace_dep ( dep, & mut manifest) ;
152
+ let mut dependencies = members
153
+ . iter ( )
154
+ . flat_map ( |manifest| {
155
+ manifest. get_sections ( ) . into_iter ( ) . flat_map ( |( _, table) | {
156
+ table
157
+ . as_table_like ( )
158
+ . unwrap ( )
159
+ . iter ( )
160
+ . map ( |( key, item) | Dependency :: from_toml ( & manifest. path , key, item) )
161
+ . collect :: < Vec < _ > > ( )
162
+ } )
163
+ } )
164
+ . collect :: < CargoResult < Vec < _ > > > ( ) ?;
165
+
166
+ // Clean up the workspace.dependencies section and replace instances of
167
+ // workspace dependencies with their definitions
168
+ if let Some ( toml_edit:: Item :: Table ( deps_table) ) = manifest
169
+ . get_mut ( "workspace" )
170
+ . and_then ( |t| t. get_mut ( "dependencies" ) )
171
+ {
172
+ deps_table. set_implicit ( true ) ;
173
+ for ( key, item) in deps_table. iter_mut ( ) {
174
+ let ws_dep = Dependency :: from_toml ( & workspace. root ( ) , key. get ( ) , item) ?;
175
+
176
+ // search for uses of this workspace dependency
177
+ let mut is_used = false ;
178
+ for dep in dependencies. iter_mut ( ) . filter ( |d| {
179
+ d. toml_key ( ) == key. get ( ) && matches ! ( d. source( ) , Some ( Source :: Workspace ( _) ) )
180
+ } ) {
181
+ // replace the reference with the definition
182
+ * dep = ws_dep. clone ( ) ;
183
+
184
+ is_used = true ;
185
+ }
186
+
187
+ if !is_used {
188
+ * item = toml_edit:: Item :: None ;
189
+ is_modified = true ;
190
+ }
149
191
}
150
192
}
151
193
152
- cargo_util:: paths:: write ( workspace. root_manifest ( ) , manifest. to_string ( ) . as_bytes ( ) ) ?;
194
+ // Clean up the profile section
195
+ //
196
+ // Example tables:
197
+ // - profile.dev.package.foo
198
+ // - profile.release.package."*"
199
+ // - profile.release.package."foo:2.1.0"
200
+ if let Some ( toml_edit:: Item :: Table ( profile_section_table) ) = manifest. get_mut ( "profile" ) {
201
+ profile_section_table. set_implicit ( true ) ;
202
+
203
+ for ( _, item) in profile_section_table. iter_mut ( ) {
204
+ if let toml_edit:: Item :: Table ( profile_table) = item {
205
+ profile_table. set_implicit ( true ) ;
206
+
207
+ if let Some ( toml_edit:: Item :: Table ( package_table) ) =
208
+ profile_table. get_mut ( "package" )
209
+ {
210
+ package_table. set_implicit ( true ) ;
211
+
212
+ for ( key, item) in package_table. iter_mut ( ) {
213
+ if !spec_has_match (
214
+ & PackageIdSpec :: parse ( key. get ( ) ) ?,
215
+ & dependencies,
216
+ workspace. config ( ) ,
217
+ ) ? {
218
+ * item = toml_edit:: Item :: None ;
219
+ is_modified = true ;
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ // Clean up the patch section
228
+ if let Some ( toml_edit:: Item :: Table ( patch_section_table) ) = manifest. get_mut ( "patch" ) {
229
+ patch_section_table. set_implicit ( true ) ;
230
+
231
+ // The key in each of the subtables is a source (either a registry or a URL)
232
+ for ( source, item) in patch_section_table. iter_mut ( ) {
233
+ if let toml_edit:: Item :: Table ( patch_table) = item {
234
+ patch_table. set_implicit ( true ) ;
235
+
236
+ for ( key, item) in patch_table. iter_mut ( ) {
237
+ let package_name =
238
+ Dependency :: from_toml ( & workspace. root_manifest ( ) , key. get ( ) , item) ?. name ;
239
+ if !source_has_match (
240
+ & package_name,
241
+ source. get ( ) ,
242
+ & dependencies,
243
+ workspace. config ( ) ,
244
+ ) ? {
245
+ * item = toml_edit:: Item :: None ;
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ // Clean up the replace section
253
+ if let Some ( toml_edit:: Item :: Table ( table) ) = manifest. get_mut ( "replace" ) {
254
+ table. set_implicit ( true ) ;
255
+
256
+ for ( key, item) in table. iter_mut ( ) {
257
+ if !spec_has_match (
258
+ & PackageIdSpec :: parse ( key. get ( ) ) ?,
259
+ & dependencies,
260
+ workspace. config ( ) ,
261
+ ) ? {
262
+ * item = toml_edit:: Item :: None ;
263
+ is_modified = true ;
264
+ }
265
+ }
266
+ }
267
+
268
+ if is_modified {
269
+ cargo_util:: paths:: write ( workspace. root_manifest ( ) , manifest. to_string ( ) . as_bytes ( ) ) ?;
270
+ }
153
271
154
272
Ok ( ( ) )
155
273
}
156
274
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
- } )
275
+ /// Check whether or not a package ID spec matches any non-workspace dependencies.
276
+ fn spec_has_match (
277
+ spec : & PackageIdSpec ,
278
+ dependencies : & [ Dependency ] ,
279
+ config : & Config ,
280
+ ) -> CargoResult < bool > {
281
+ for dep in dependencies {
282
+ if spec. name ( ) . as_str ( ) != & dep. name {
283
+ continue ;
284
+ }
285
+
286
+ let version_matches = match ( spec. version ( ) , dep. version ( ) ) {
287
+ ( Some ( v) , Some ( vq) ) => semver:: VersionReq :: parse ( vq) ?. matches ( v) ,
288
+ ( Some ( _) , None ) => false ,
289
+ ( None , None | Some ( _) ) => true ,
290
+ } ;
291
+ if !version_matches {
292
+ continue ;
293
+ }
294
+
295
+ match dep. source_id ( config) ? {
296
+ MaybeWorkspace :: Other ( source_id) => {
297
+ if spec. url ( ) . map ( |u| u == source_id. url ( ) ) . unwrap_or ( true ) {
298
+ return Ok ( true ) ;
299
+ }
300
+ }
301
+ MaybeWorkspace :: Workspace ( _) => { }
302
+ }
303
+ }
304
+
305
+ Ok ( false )
170
306
}
171
307
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) ;
308
+ /// Check whether or not a source (URL or registry name) matches any non-workspace dependencies.
309
+ fn source_has_match (
310
+ name : & str ,
311
+ source : & str ,
312
+ dependencies : & [ Dependency ] ,
313
+ config : & Config ,
314
+ ) -> CargoResult < bool > {
315
+ for dep in dependencies {
316
+ if & dep. name != name {
317
+ continue ;
318
+ }
319
+
320
+ match dep. source_id ( config) ? {
321
+ MaybeWorkspace :: Other ( source_id) => {
322
+ if source_id. is_registry ( ) {
323
+ if source_id. display_registry_name ( ) == source
324
+ || source_id. url ( ) . as_str ( ) == source
325
+ {
326
+ return Ok ( true ) ;
327
+ }
328
+ } else if source_id. is_git ( ) {
329
+ if source_id. url ( ) . as_str ( ) == source {
330
+ return Ok ( true ) ;
331
+ }
332
+ }
333
+ }
334
+ MaybeWorkspace :: Workspace ( _) => { }
335
+ }
180
336
}
337
+
338
+ Ok ( false )
181
339
}
0 commit comments