1
1
//! Core of cargo-remove command
2
2
3
+ use crate :: core:: Dependency ;
3
4
use crate :: core:: Package ;
5
+ use crate :: core:: Workspace ;
4
6
use crate :: util:: toml_mut:: manifest:: DepTable ;
5
7
use crate :: util:: toml_mut:: manifest:: LocalManifest ;
6
8
use crate :: CargoResult ;
@@ -11,6 +13,8 @@ use crate::Config;
11
13
pub struct RemoveOptions < ' a > {
12
14
/// Configuration information for Cargo operations
13
15
pub config : & ' a Config ,
16
+ /// Workspace in which the operation is occurring.
17
+ pub workspace : & ' a Workspace < ' a > ,
14
18
/// Package to remove dependencies from
15
19
pub spec : & ' a Package ,
16
20
/// Dependencies to remove
@@ -33,7 +37,15 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
33
37
let manifest_path = options. spec . manifest_path ( ) . to_path_buf ( ) ;
34
38
let mut manifest = LocalManifest :: try_new ( & manifest_path) ?;
35
39
40
+ let mut to_remove_workspace = vec ! [ ] ;
41
+
36
42
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
+
37
49
let section = if dep_table. len ( ) >= 3 {
38
50
format ! ( "{} for target `{}`" , & dep_table[ 2 ] , & dep_table[ 1 ] )
39
51
} else {
@@ -46,20 +58,119 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
46
58
47
59
manifest. remove_from_table ( & dep_table, dep) ?;
48
60
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.
52
63
manifest. gc_dep ( dep) ;
53
64
}
54
65
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
+
55
107
if options. dry_run {
56
108
options
57
109
. config
58
110
. shell ( )
59
111
. warn ( "aborting remove due to dry run" ) ?;
60
112
} else {
61
113
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
+ }
62
121
}
63
122
64
123
Ok ( ( ) )
65
124
}
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