11use cargo:: core:: dependency:: DepKind ;
2+ use cargo:: core:: PackageIdSpec ;
23use cargo:: core:: Workspace ;
34use cargo:: ops:: cargo_remove:: remove;
45use cargo:: ops:: cargo_remove:: RemoveOptions ;
56use cargo:: ops:: resolve_ws;
67use cargo:: util:: command_prelude:: * ;
78use 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 ;
812use cargo:: util:: toml_mut:: manifest:: DepTable ;
913use cargo:: util:: toml_mut:: manifest:: LocalManifest ;
1014use cargo:: CargoResult ;
@@ -86,7 +90,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
8690 . get_many :: < String > ( "dependencies" )
8791 . expect ( "required(true)" )
8892 . cloned ( )
89- . collect ( ) ;
93+ . collect :: < Vec < _ > > ( ) ;
9094
9195 let section = parse_section ( args) ;
9296
@@ -100,8 +104,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
100104 remove ( & options) ?;
101105
102106 if !dry_run {
103- // Clean up workspace dependencies
104- gc_workspace ( & workspace, & options . dependencies ) ?;
107+ // Clean up the workspace
108+ gc_workspace ( & workspace) ?;
105109
106110 // Reload the workspace since we've changed dependencies
107111 let ws = args. workspace ( config) ?;
@@ -133,8 +137,9 @@ fn parse_section(args: &ArgMatches) -> DepTable {
133137 table
134138}
135139
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 < ( ) > {
138143 let mut manifest: toml_edit:: Document =
139144 cargo_util:: paths:: read ( workspace. root_manifest ( ) ) ?. parse ( ) ?;
140145
@@ -143,9 +148,104 @@ fn gc_workspace(workspace: &Workspace<'_>, dependencies: &[String]) -> CargoResu
143148 . map ( |p| LocalManifest :: try_new ( p. manifest_path ( ) ) )
144149 . collect :: < CargoResult < Vec < _ > > > ( ) ?;
145150
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+ }
149249 }
150250 }
151251
@@ -154,28 +254,72 @@ fn gc_workspace(workspace: &Workspace<'_>, dependencies: &[String]) -> CargoResu
154254 Ok ( ( ) )
155255}
156256
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 )
170292}
171293
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+ }
180322 }
323+
324+ Ok ( false )
181325}
0 commit comments