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,18 @@ 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 is_root = options. spec . manifest_path ( ) == options. workspace . root_manifest ( ) ;
41
+ let mut parent_manifest = if !is_root {
42
+ let data = cargo_util:: paths:: read ( options. workspace . root_manifest ( ) ) ?;
43
+ let manifest: toml_edit:: Document = data. parse ( ) ?;
44
+ Some ( manifest)
45
+ } else {
46
+ None
47
+ } ;
48
+
36
49
for dep in & options. dependencies {
50
+ let is_workspace_dep = dependency_is_workspace ( dep, & dep_table, & manifest) ;
51
+
37
52
let section = if dep_table. len ( ) >= 3 {
38
53
format ! ( "{} for target `{}`" , & dep_table[ 2 ] , & dep_table[ 1 ] )
39
54
} else {
@@ -50,6 +65,23 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
50
65
// crate, then we need to drop any explicitly activated features on
51
66
// that crate.
52
67
manifest. gc_dep ( dep) ;
68
+
69
+ // If this was the last instance of a workspace dependency, remove it
70
+ // from the workspace dependencies.
71
+ if is_workspace_dep
72
+ && !dependency_in_workspace ( dep, & options. section , options. spec , options. workspace )
73
+ {
74
+ let workspace_manifest = match & mut parent_manifest {
75
+ Some ( parent_manifest) => parent_manifest,
76
+ None => & mut manifest. data ,
77
+ } ;
78
+ remove_dependency_from_workspace ( dep, workspace_manifest) ;
79
+
80
+ options
81
+ . config
82
+ . shell ( )
83
+ . status ( "Removing" , format ! ( "{dep} from workspace dependencies" ) ) ?;
84
+ }
53
85
}
54
86
55
87
if options. dry_run {
@@ -59,7 +91,76 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
59
91
. warn ( "aborting remove due to dry run" ) ?;
60
92
} else {
61
93
manifest. write ( ) ?;
94
+
95
+ if let Some ( parent_manifest) = parent_manifest {
96
+ cargo_util:: paths:: write (
97
+ options. workspace . root_manifest ( ) ,
98
+ parent_manifest. to_string ( ) . as_bytes ( ) ,
99
+ ) ?;
100
+ }
62
101
}
63
102
64
103
Ok ( ( ) )
65
104
}
105
+
106
+ /// Get whether or not a dependency is marked as `workspace`.
107
+ fn dependency_is_workspace ( dep : & str , dep_table : & [ String ] , manifest : & LocalManifest ) -> bool {
108
+ if let Ok ( toml_edit:: Item :: Table ( table) ) = manifest. get_table ( dep_table) {
109
+ let value = table. get ( dep) . and_then ( |i| i. get ( "workspace" ) ) ;
110
+ if let Some ( toml_edit:: Item :: Value ( value) ) = value {
111
+ return value. as_bool ( ) == Some ( true ) ;
112
+ }
113
+ }
114
+
115
+ false
116
+ }
117
+
118
+ /// Get whether or not a dependency is depended upon in a workspace.
119
+ fn dependency_in_workspace (
120
+ dep : & str ,
121
+ dep_table : & DepTable ,
122
+ exclude : & Package ,
123
+ workspace : & Workspace < ' _ > ,
124
+ ) -> bool {
125
+ workspace. members ( ) . any ( |p| {
126
+ let manifest = LocalManifest :: try_new ( p. manifest_path ( ) ) . unwrap ( ) ; // TODO handle error
127
+ p. dependencies ( )
128
+ . iter ( )
129
+ . filter ( |d| d. package_name ( ) . as_str ( ) == dep)
130
+ . map ( |d| ( d, dependency_to_table ( d) ) )
131
+ // ignore the dep we could have just removed
132
+ . filter ( |( _, t) | !( p == exclude && t == dep_table) )
133
+ // only dependencies marked workspace
134
+ . filter ( |( d, t) | {
135
+ // information about workspace dependencies is not preserved at
136
+ // this stage, so we have to manually read the TOML file
137
+ let dep_table = t
138
+ . to_table ( )
139
+ . into_iter ( )
140
+ . map ( String :: from)
141
+ . collect :: < Vec < _ > > ( ) ;
142
+ dependency_is_workspace ( & d. package_name ( ) , & dep_table, & manifest)
143
+ } )
144
+ . next ( )
145
+ . is_some ( )
146
+ } )
147
+ }
148
+
149
+ fn dependency_to_table ( dep : & Dependency ) -> DepTable {
150
+ let mut dep_table = DepTable :: new ( ) . set_kind ( dep. kind ( ) ) ;
151
+ if let Some ( target) = dep. platform ( ) . map ( |p| format ! ( "{p}" ) ) {
152
+ dep_table = dep_table. set_target ( target) ;
153
+ }
154
+ dep_table
155
+ }
156
+
157
+ /// Remove a dependency from a virtual workspace.
158
+ fn remove_dependency_from_workspace ( dep : & str , virtual_manifest : & mut toml_edit:: Document ) {
159
+ if let Some ( toml_edit:: Item :: Table ( table) ) = virtual_manifest
160
+ . get_mut ( "workspace" )
161
+ . and_then ( |t| t. get_mut ( "dependencies" ) )
162
+ {
163
+ table. set_implicit ( true ) ;
164
+ table. remove ( dep) ;
165
+ }
166
+ }
0 commit comments