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