@@ -143,26 +143,11 @@ pub fn do_add_to_path(methods: &[PathUpdateMethod]) -> Result<()> {
143143 use winreg:: enums:: { RegType , HKEY_CURRENT_USER , KEY_READ , KEY_WRITE } ;
144144 use winreg:: { RegKey , RegValue } ;
145145
146- let old_path = if let Some ( s) = get_windows_path_var ( ) ? {
147- s
148- } else {
149- // Non-unicode path
150- return Ok ( ( ) ) ;
146+ let new_path = match _with_path_cargo_home_bin ( _add_to_path) ? {
147+ Some ( new_path) => new_path,
148+ None => return Ok ( ( ) ) , // No need to set the path
151149 } ;
152150
153- let mut new_path = utils:: cargo_home ( ) ?
154- . join ( "bin" )
155- . to_string_lossy ( )
156- . into_owned ( ) ;
157- if old_path. contains ( & new_path) {
158- return Ok ( ( ) ) ;
159- }
160-
161- if !old_path. is_empty ( ) {
162- new_path. push_str ( ";" ) ;
163- new_path. push_str ( & old_path) ;
164- }
165-
166151 let root = RegKey :: predef ( HKEY_CURRENT_USER ) ;
167152 let environment = root
168153 . open_subkey_with_flags ( "Environment" , KEY_READ | KEY_WRITE )
@@ -222,8 +207,23 @@ fn get_windows_path_var() -> Result<Option<String>> {
222207 }
223208}
224209
210+ // Returns None if the existing old_path does not need changing, otherwise
211+ // prepends the path_str to old_path, handling empty old_path appropriately.
212+ fn _add_to_path ( old_path : & str , path_str : String ) -> Option < String > {
213+ if old_path. is_empty ( ) {
214+ Some ( path_str)
215+ } else if old_path. contains ( & path_str) {
216+ None
217+ } else {
218+ let mut new_path = path_str. clone ( ) ;
219+ new_path. push_str ( ";" ) ;
220+ new_path. push_str ( & old_path) ;
221+ Some ( new_path)
222+ }
223+ }
224+
225225// Returns None if the existing old_path does not need changing
226- fn _remove_from_path ( old_path : & str , path_str : & str ) -> Option < String > {
226+ fn _remove_from_path ( old_path : & str , path_str : String ) -> Option < String > {
227227 let idx = old_path. find ( & path_str) ?;
228228 // If there's a trailing semicolon (likely, since we probably added one
229229 // during install), include that in the substring to remove. We don't search
@@ -244,20 +244,16 @@ fn _remove_from_path(old_path: &str, path_str: &str) -> Option<String> {
244244 Some ( new_path)
245245}
246246
247- fn _path_without_cargo_home_bin ( ) -> Result < Option < String > > {
248- let old_path = if let Some ( s) = get_windows_path_var ( ) ? {
249- s
250- } else {
251- // Non-unicode path
252- return Ok ( None ) ;
253- } ;
254-
247+ fn _with_path_cargo_home_bin < F > ( f : F ) -> Result < Option < String > >
248+ where
249+ F : FnOnce ( & str , String ) -> Option < String > ,
250+ {
251+ let windows_path = get_windows_path_var ( ) ?;
255252 let path_str = utils:: cargo_home ( ) ?
256253 . join ( "bin" )
257254 . to_string_lossy ( )
258255 . into_owned ( ) ;
259-
260- Ok ( _remove_from_path ( & old_path, & path_str) )
256+ Ok ( windows_path. and_then ( |old_path| f ( & old_path, path_str) ) )
261257}
262258
263259pub fn do_remove_from_path ( methods : & [ PathUpdateMethod ] ) -> Result < ( ) > {
@@ -271,7 +267,7 @@ pub fn do_remove_from_path(methods: &[PathUpdateMethod]) -> Result<()> {
271267 use winreg:: enums:: { RegType , HKEY_CURRENT_USER , KEY_READ , KEY_WRITE } ;
272268 use winreg:: { RegKey , RegValue } ;
273269
274- let new_path = match _path_without_cargo_home_bin ( ) ? {
270+ let new_path = match _with_path_cargo_home_bin ( _remove_from_path ) ? {
275271 Some ( new_path) => new_path,
276272 None => return Ok ( ( ) ) , // No need to set the path
277273 } ;
@@ -503,9 +499,26 @@ mod tests {
503499 }
504500
505501 #[ test]
506- fn windows_uninstall_doesnt_mess_with_a_non_unicode_path ( ) {
502+ fn windows_install_does_not_add_path_twice ( ) {
503+ assert_eq ! (
504+ None ,
505+ super :: _add_to_path(
506+ r"c:\users\example\.cargo\bin;foo" ,
507+ r"c:\users\example\.cargo\bin" . into( )
508+ )
509+ ) ;
510+ }
511+
512+ #[ test]
513+ fn windows_doesnt_mess_with_a_non_unicode_path ( ) {
507514 // This writes an error, so we want a sink for it.
508- let tp = Box :: new ( currentprocess:: TestProcess :: default ( ) ) ;
515+ let tp = Box :: new ( currentprocess:: TestProcess {
516+ vars : [ ( "HOME" . to_string ( ) , "/unused" . to_string ( ) ) ]
517+ . iter ( )
518+ . cloned ( )
519+ . collect ( ) ,
520+ ..Default :: default ( )
521+ } ) ;
509522 with_registry_edits ( & || {
510523 currentprocess:: with ( tp. clone ( ) , || {
511524 let root = RegKey :: predef ( HKEY_CURRENT_USER ) ;
@@ -522,7 +535,10 @@ mod tests {
522535 } ;
523536 environment. set_raw_value ( "PATH" , & reg_value) . unwrap ( ) ;
524537 // Ok(None) signals no change to the PATH setting layer
525- assert_eq ! ( None , super :: _path_without_cargo_home_bin( ) . unwrap( ) ) ;
538+ fn panic ( _: & str , _: String ) -> Option < String > {
539+ panic ! ( "called" ) ;
540+ }
541+ assert_eq ! ( None , super :: _with_path_cargo_home_bin( panic) . unwrap( ) ) ;
526542 } )
527543 } ) ;
528544 assert_eq ! (
@@ -538,7 +554,7 @@ mod tests {
538554 "foo" ,
539555 super :: _remove_from_path(
540556 r"c:\users\example\.cargo\bin;foo" ,
541- r"c:\users\example\.cargo\bin"
557+ r"c:\users\example\.cargo\bin" . into ( )
542558 )
543559 . unwrap( )
544560 )
@@ -550,7 +566,7 @@ mod tests {
550566 "foo" ,
551567 super :: _remove_from_path(
552568 r"foo;c:\users\example\.cargo\bin" ,
553- r"c:\users\example\.cargo\bin"
569+ r"c:\users\example\.cargo\bin" . into ( )
554570 )
555571 . unwrap( )
556572 )
0 commit comments