1
- use std:: collections:: VecDeque ;
1
+ use std:: collections:: { HashSet , VecDeque } ;
2
2
use std:: env;
3
+ use std:: ffi:: OsStr ;
3
4
use std:: path:: PathBuf ;
4
5
use std:: process:: Command ;
5
6
6
7
use crate :: process;
7
8
8
9
pub const RUST_RECURSION_COUNT_MAX : u32 = 20 ;
9
10
11
+ /// This can be removed when https://github.com/rust-lang/rust/issues/44434 is
12
+ /// stablised.
13
+ pub ( crate ) trait ProcessEnvs {
14
+ fn env < K , V > ( & mut self , key : K , val : V ) -> & mut Self
15
+ where
16
+ Self : Sized ,
17
+ K : AsRef < OsStr > ,
18
+ V : AsRef < OsStr > ;
19
+ }
20
+
21
+ impl ProcessEnvs for Command {
22
+ fn env < K , V > ( & mut self , key : K , val : V ) -> & mut Self
23
+ where
24
+ Self : Sized ,
25
+ K : AsRef < OsStr > ,
26
+ V : AsRef < OsStr > ,
27
+ {
28
+ self . env ( key, val)
29
+ }
30
+ }
31
+
10
32
#[ allow( unused) ]
11
- fn append_path ( name : & str , value : Vec < PathBuf > , cmd : & mut Command ) {
33
+ fn append_path < E : ProcessEnvs > ( name : & str , value : Vec < PathBuf > , cmd : & mut E ) {
12
34
let old_value = process ( ) . var_os ( name) ;
13
35
let mut parts: Vec < PathBuf > ;
14
36
if let Some ( ref v) = old_value {
15
- parts = env:: split_paths ( v) . collect ( ) ;
16
- parts. extend ( value) ;
37
+ let old_paths : Vec < PathBuf > = env:: split_paths ( v) . collect :: < Vec < _ > > ( ) ;
38
+ parts = concat_uniq_paths ( old_paths , value) ;
17
39
} else {
18
40
parts = value;
19
41
}
@@ -22,7 +44,7 @@ fn append_path(name: &str, value: Vec<PathBuf>, cmd: &mut Command) {
22
44
}
23
45
}
24
46
25
- pub ( crate ) fn prepend_path ( name : & str , prepend : Vec < PathBuf > , cmd : & mut Command ) {
47
+ pub ( crate ) fn prepend_path < E : ProcessEnvs > ( name : & str , prepend : Vec < PathBuf > , cmd : & mut E ) {
26
48
let old_value = process ( ) . var_os ( name) ;
27
49
let parts = if let Some ( ref v) = old_value {
28
50
let mut tail = env:: split_paths ( v) . collect :: < VecDeque < _ > > ( ) ;
@@ -50,3 +72,167 @@ pub(crate) fn inc(name: &str, cmd: &mut Command) {
50
72
51
73
cmd. env ( name, ( old_value + 1 ) . to_string ( ) ) ;
52
74
}
75
+
76
+ fn concat_uniq_paths ( fst_paths : Vec < PathBuf > , snd_paths : Vec < PathBuf > ) -> Vec < PathBuf > {
77
+ let deduped_fst_paths = dedupe_with_preserved_order ( fst_paths) ;
78
+ let deduped_snd_paths = dedupe_with_preserved_order ( snd_paths) ;
79
+
80
+ let vec_fst_paths: Vec < _ > = deduped_fst_paths. into_iter ( ) . collect ( ) ;
81
+
82
+ let mut unified_paths;
83
+ unified_paths = vec_fst_paths. clone ( ) ;
84
+ unified_paths. extend (
85
+ deduped_snd_paths
86
+ . into_iter ( )
87
+ . filter ( |v| !vec_fst_paths. contains ( v) )
88
+ . collect :: < Vec < _ > > ( ) ,
89
+ ) ;
90
+
91
+ unified_paths
92
+ }
93
+
94
+ fn dedupe_with_preserved_order ( paths : Vec < PathBuf > ) -> Vec < PathBuf > {
95
+ let mut uniq_paths: Vec < PathBuf > = Vec :: new ( ) ;
96
+ let mut seen_paths: HashSet < PathBuf > = HashSet :: new ( ) ;
97
+
98
+ for path in & paths {
99
+ if !seen_paths. contains ( path) {
100
+ seen_paths. insert ( path. to_path_buf ( ) ) ;
101
+ uniq_paths. push ( path. to_path_buf ( ) ) ;
102
+ }
103
+ }
104
+
105
+ uniq_paths
106
+ }
107
+
108
+ #[ cfg( test) ]
109
+ mod tests {
110
+ use super :: * ;
111
+ use crate :: currentprocess;
112
+ use crate :: test:: { with_saved_path, Env } ;
113
+
114
+ use super :: ProcessEnvs ;
115
+ use std:: collections:: HashMap ;
116
+ use std:: ffi:: { OsStr , OsString } ;
117
+
118
+ #[ derive( Default ) ]
119
+ struct TestCommand {
120
+ envs : HashMap < OsString , Option < OsString > > ,
121
+ }
122
+
123
+ impl ProcessEnvs for TestCommand {
124
+ fn env < K , V > ( & mut self , key : K , val : V ) -> & mut Self
125
+ where
126
+ Self : Sized ,
127
+ K : AsRef < OsStr > ,
128
+ V : AsRef < OsStr > ,
129
+ {
130
+ self . envs
131
+ . insert ( key. as_ref ( ) . to_owned ( ) , Some ( val. as_ref ( ) . to_owned ( ) ) ) ;
132
+ self
133
+ }
134
+ }
135
+
136
+ #[ test]
137
+ fn deduplicate_and_concat_paths ( ) {
138
+ let mut old_paths = vec ! [ ] ;
139
+
140
+ let z = OsString :: from ( "/home/z/.cargo/bin" ) ;
141
+ let path_z = PathBuf :: from ( z) ;
142
+ old_paths. push ( path_z) ;
143
+
144
+ let a = OsString :: from ( "/home/a/.cargo/bin" ) ;
145
+ let path_a = PathBuf :: from ( a) ;
146
+ old_paths. push ( path_a) ;
147
+
148
+ let _a = OsString :: from ( "/home/a/.cargo/bin" ) ;
149
+ let _path_a = PathBuf :: from ( _a) ;
150
+ old_paths. push ( _path_a) ;
151
+
152
+ let mut new_paths = vec ! [ ] ;
153
+
154
+ let n = OsString :: from ( "/home/n/.cargo/bin" ) ;
155
+ let path_n = PathBuf :: from ( n) ;
156
+ new_paths. push ( path_n) ;
157
+
158
+ let g = OsString :: from ( "/home/g/.cargo/bin" ) ;
159
+ let path_g = PathBuf :: from ( g) ;
160
+ new_paths. push ( path_g) ;
161
+
162
+ let _g = OsString :: from ( "/home/g/.cargo/bin" ) ;
163
+ let _path_g = PathBuf :: from ( _g) ;
164
+ new_paths. push ( _path_g) ;
165
+
166
+ let _z = OsString :: from ( "/home/z/.cargo/bin" ) ;
167
+ let path_z = PathBuf :: from ( _z) ;
168
+ old_paths. push ( path_z) ;
169
+
170
+ let mut unified_paths: Vec < PathBuf > = vec ! [ ] ;
171
+ let zpath = OsString :: from ( "/home/z/.cargo/bin" ) ;
172
+ let _zpath = PathBuf :: from ( zpath) ;
173
+ unified_paths. push ( _zpath) ;
174
+ let apath = OsString :: from ( "/home/a/.cargo/bin" ) ;
175
+ let _apath = PathBuf :: from ( apath) ;
176
+ unified_paths. push ( _apath) ;
177
+ let npath = OsString :: from ( "/home/n/.cargo/bin" ) ;
178
+ let _npath = PathBuf :: from ( npath) ;
179
+ unified_paths. push ( _npath) ;
180
+ let gpath = OsString :: from ( "/home/g/.cargo/bin" ) ;
181
+ let _gpath = PathBuf :: from ( gpath) ;
182
+ unified_paths. push ( _gpath) ;
183
+
184
+ assert_eq ! ( concat_uniq_paths( old_paths, new_paths) , unified_paths) ;
185
+ }
186
+
187
+ #[ test]
188
+ fn prepend_unique_path ( ) {
189
+ let mut vars = HashMap :: new ( ) ;
190
+ vars. env (
191
+ "PATH" ,
192
+ env:: join_paths ( vec ! [ "/home/a/.cargo/bin" , "/home/b/.cargo/bin" ] . iter ( ) ) . unwrap ( ) ,
193
+ ) ;
194
+ let tp = Box :: new ( currentprocess:: TestProcess {
195
+ vars,
196
+ ..Default :: default ( )
197
+ } ) ;
198
+ with_saved_path ( & || {
199
+ currentprocess:: with ( tp. clone ( ) , || {
200
+ let mut path_entries = vec ! [ ] ;
201
+ let mut cmd = TestCommand :: default ( ) ;
202
+
203
+ let a = OsString :: from ( "/home/a/.cargo/bin" ) ;
204
+ let path_a = PathBuf :: from ( a) ;
205
+ path_entries. push ( path_a) ;
206
+
207
+ let _a = OsString :: from ( "/home/a/.cargo/bin" ) ;
208
+ let _path_a = PathBuf :: from ( _a) ;
209
+ path_entries. push ( _path_a) ;
210
+
211
+ let z = OsString :: from ( "/home/z/.cargo/bin" ) ;
212
+ let path_z = PathBuf :: from ( z) ;
213
+ path_entries. push ( path_z) ;
214
+
215
+ prepend_path ( "PATH" , path_entries, & mut cmd) ;
216
+ let envs: Vec < _ > = cmd. envs . iter ( ) . collect ( ) ;
217
+
218
+ assert_eq ! (
219
+ envs,
220
+ & [ (
221
+ & OsString :: from( "PATH" ) ,
222
+ & Some (
223
+ env:: join_paths(
224
+ vec![
225
+ "/home/z/.cargo/bin" ,
226
+ "/home/a/.cargo/bin" ,
227
+ "/home/b/.cargo/bin"
228
+ ]
229
+ . iter( )
230
+ )
231
+ . unwrap( )
232
+ )
233
+ ) , ]
234
+ ) ;
235
+ } ) ;
236
+ } ) ;
237
+ }
238
+ }
0 commit comments