11//! Core of cargo-remove command
22
3+ use crate :: core:: Dependency ;
34use crate :: core:: Package ;
5+ use crate :: core:: Workspace ;
46use crate :: util:: toml_mut:: manifest:: DepTable ;
57use crate :: util:: toml_mut:: manifest:: LocalManifest ;
68use crate :: CargoResult ;
@@ -11,6 +13,8 @@ use crate::Config;
1113pub struct RemoveOptions < ' a > {
1214 /// Configuration information for Cargo operations
1315 pub config : & ' a Config ,
16+ /// Workspace in which the operation is occurring.
17+ pub workspace : & ' a Workspace < ' a > ,
1418 /// Package to remove dependencies from
1519 pub spec : & ' a Package ,
1620 /// Dependencies to remove
@@ -33,7 +37,15 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
3337 let manifest_path = options. spec . manifest_path ( ) . to_path_buf ( ) ;
3438 let mut manifest = LocalManifest :: try_new ( & manifest_path) ?;
3539
40+ let mut to_remove_workspace = vec ! [ ] ;
41+
3642 for dep in & options. dependencies {
43+ // If this was the last instance of a workspace dependency, queue it to be removed from the
44+ // workspace dependencies.
45+ if dep_is_workspace ( dep, & dep_table, & manifest) {
46+ to_remove_workspace. push ( dep) ;
47+ }
48+
3749 let section = if dep_table. len ( ) >= 3 {
3850 format ! ( "{} for target `{}`" , & dep_table[ 2 ] , & dep_table[ 1 ] )
3951 } else {
@@ -46,20 +58,119 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
4658
4759 manifest. remove_from_table ( & dep_table, dep) ?;
4860
49- // Now that we have removed the crate, if that was the last reference to that
50- // crate, then we need to drop any explicitly activated features on
51- // that crate.
61+ // Now that we have removed the crate, if that was the last reference to that crate, then we
62+ // need to drop any explicitly activated features on that crate.
5263 manifest. gc_dep ( dep) ;
5364 }
5465
66+
67+ // remove from workspace dependencies
68+
69+ let mut ws_manifest = None ;
70+
71+ if !to_remove_workspace. is_empty ( ) {
72+ // read the packages in the workspace, substituting in the modified manifest
73+ let members = options
74+ . workspace
75+ . members ( )
76+ . map ( |p| {
77+ if p. manifest_path ( ) == manifest_path {
78+ Ok ( ( p, manifest. clone ( ) ) )
79+ } else {
80+ LocalManifest :: try_new ( p. manifest_path ( ) ) . map ( |m| ( p, m) )
81+ }
82+ } )
83+ . collect :: < CargoResult < Vec < _ > > > ( ) ?;
84+
85+ if options. workspace . root_manifest ( ) != manifest_path {
86+ let manifest: toml_edit:: Document =
87+ cargo_util:: paths:: read ( options. workspace . root_manifest ( ) ) ?. parse ( ) ?;
88+ ws_manifest = Some ( manifest) ;
89+ }
90+
91+ let ws_manifest = match & mut ws_manifest {
92+ Some ( manifest) => manifest,
93+ None => & mut manifest. data ,
94+ } ;
95+
96+ for dep in to_remove_workspace {
97+ if !dep_in_workspace ( dep, & members) {
98+ remove_workspace_dep ( dep, ws_manifest) ;
99+ options
100+ . config
101+ . shell ( )
102+ . status ( "Removing" , format ! ( "{dep} from workspace dependencies" ) ) ?;
103+ }
104+ }
105+ }
106+
55107 if options. dry_run {
56108 options
57109 . config
58110 . shell ( )
59111 . warn ( "aborting remove due to dry run" ) ?;
60112 } else {
61113 manifest. write ( ) ?;
114+
115+ if let Some ( ws_manifest) = ws_manifest {
116+ cargo_util:: paths:: write (
117+ options. workspace . root_manifest ( ) ,
118+ ws_manifest. to_string ( ) . as_bytes ( ) ,
119+ ) ?;
120+ }
62121 }
63122
64123 Ok ( ( ) )
65124}
125+
126+ /// Get whether or not a dependency is marked as `workspace`.
127+ fn dep_is_workspace ( dep : & str , dep_table : & [ String ] , manifest : & LocalManifest ) -> bool {
128+ if let Ok ( toml_edit:: Item :: Table ( table) ) = manifest. get_table ( dep_table) {
129+ let value = table. get ( dep) . and_then ( |i| i. get ( "workspace" ) ) ;
130+ if let Some ( toml_edit:: Item :: Value ( value) ) = value {
131+ return value. as_bool ( ) == Some ( true ) ;
132+ }
133+ }
134+
135+ false
136+ }
137+
138+ /// Get whether or not a dependency is depended upon in a workspace.
139+ fn dep_in_workspace ( dep : & str , members : & [ ( & Package , LocalManifest ) ] ) -> bool {
140+ members. iter ( ) . any ( |( pkg, manifest) | {
141+ pkg. dependencies ( )
142+ . iter ( )
143+ . filter ( |d| d. name_in_toml ( ) == dep)
144+ . map ( |d| ( d, dep_to_table ( d) ) )
145+ . any ( |( d, t) | {
146+ // Information about workspace dependencies is not preserved at this stage, so we
147+ // have to manually read from the TOML manifest
148+ let dep_table = t
149+ . to_table ( )
150+ . into_iter ( )
151+ . map ( String :: from)
152+ . collect :: < Vec < _ > > ( ) ;
153+ dep_is_workspace ( & d. package_name ( ) , & dep_table, manifest)
154+ } )
155+ } )
156+ }
157+
158+ /// Find the corresponding [`DepTable`] for a [`Dependency`].
159+ fn dep_to_table ( dep : & Dependency ) -> DepTable {
160+ let mut dep_table = DepTable :: new ( ) . set_kind ( dep. kind ( ) ) ;
161+ if let Some ( target) = dep. platform ( ) . map ( |p| p. to_string ( ) ) {
162+ dep_table = dep_table. set_target ( target) ;
163+ }
164+ dep_table
165+ }
166+
167+ /// Remove a dependency from a workspace manifest.
168+ fn remove_workspace_dep ( dep : & str , ws_manifest : & mut toml_edit:: Document ) {
169+ if let Some ( toml_edit:: Item :: Table ( table) ) = ws_manifest
170+ . get_mut ( "workspace" )
171+ . and_then ( |t| t. get_mut ( "dependencies" ) )
172+ {
173+ table. set_implicit ( true ) ;
174+ table. remove ( dep) ;
175+ }
176+ }
0 commit comments