1- use std:: collections:: VecDeque ;
1+ use std:: collections:: { HashSet , VecDeque } ;
22use std:: env;
3+ use std:: ffi:: OsStr ;
34use std:: path:: PathBuf ;
45use std:: process:: Command ;
56
67use crate :: process;
78
89pub const RUST_RECURSION_COUNT_MAX : u32 = 20 ;
910
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+
1032#[ 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 ) {
1234 let old_value = process ( ) . var_os ( name) ;
1335 let mut parts: Vec < PathBuf > ;
1436 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) ;
1739 } else {
1840 parts = value;
1941 }
@@ -22,7 +44,7 @@ fn append_path(name: &str, value: Vec<PathBuf>, cmd: &mut Command) {
2244 }
2345}
2446
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 ) {
2648 let old_value = process ( ) . var_os ( name) ;
2749 let parts = if let Some ( ref v) = old_value {
2850 let mut tail = env:: split_paths ( v) . collect :: < VecDeque < _ > > ( ) ;
@@ -50,3 +72,167 @@ pub(crate) fn inc(name: &str, cmd: &mut Command) {
5072
5173 cmd. env ( name, ( old_value + 1 ) . to_string ( ) ) ;
5274}
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