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