@@ -11,20 +11,20 @@ pub fn exec(meta: &mut DvmMeta) -> Result<()> {
1111 // Init enviroments if need
1212 // actually set DVM_DIR env var if not exist.
1313 let home_path = dvm_root ( ) ;
14- set_env :: check_or_set ( "DVM_DIR" , home_path. to_str ( ) . unwrap ( ) ) . unwrap ( ) ;
15- let path = set_env :: get ( "PATH" ) . unwrap ( ) ;
14+ check_or_set_env ( "DVM_DIR" , home_path. to_str ( ) . unwrap ( ) ) ? ;
15+ let path = get_env ( "PATH" ) ? ;
1616 let looking_for = deno_bin_path ( ) . parent ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . to_string ( ) ;
1717 let current = which:: which ( "deno" ) ;
1818
1919 if let Ok ( current) = current {
2020 if current. to_str ( ) . unwrap ( ) . starts_with ( & looking_for) {
2121 println ! ( "{}" , "DVM deno bin is already set correctly." . green( ) ) ;
2222 } else {
23- set_env :: prepend ( "PATH" , looking_for. as_str ( ) ) . unwrap ( ) ;
23+ prepend_env_path ( looking_for. as_str ( ) ) ? ;
2424 println ! ( "{}" , "Please restart your shell of choice to take effects." . red( ) ) ;
2525 }
26- } else if !path . contains ( looking_for. as_str ( ) ) {
27- set_env :: prepend ( "PATH" , looking_for. as_str ( ) ) . unwrap ( ) ;
26+ } else if !env_path_contains ( & path , looking_for. as_str ( ) ) {
27+ prepend_env_path ( looking_for. as_str ( ) ) ? ;
2828 println ! ( "{}" , "Please restart your shell of choice to take effects." . red( ) ) ;
2929 }
3030
@@ -65,3 +65,133 @@ pub fn exec(meta: &mut DvmMeta) -> Result<()> {
6565 println ! ( "{}" , "All fixes applied, DVM is ready to use." . green( ) ) ;
6666 Ok ( ( ) )
6767}
68+
69+ #[ cfg( not( windows) ) ]
70+ fn check_or_set_env ( name : & str , value : & str ) -> Result < ( ) > {
71+ set_env:: check_or_set ( name, value) . map_err ( Into :: into)
72+ }
73+
74+ #[ cfg( not( windows) ) ]
75+ fn get_env ( name : & str ) -> Result < String > {
76+ set_env:: get ( name) . map_err ( Into :: into)
77+ }
78+
79+ #[ cfg( not( windows) ) ]
80+ fn prepend_env_path ( value : & str ) -> Result < ( ) > {
81+ set_env:: prepend ( "PATH" , value) . map_err ( Into :: into)
82+ }
83+
84+ #[ cfg( not( windows) ) ]
85+ fn env_path_contains ( path : & str , value : & str ) -> bool {
86+ path. contains ( value)
87+ }
88+
89+ #[ cfg( windows) ]
90+ fn check_or_set_env ( name : & str , value : & str ) -> Result < ( ) > {
91+ if std:: env:: var_os ( name) . is_none ( ) {
92+ set_user_env ( name, value) ?;
93+ }
94+ Ok ( ( ) )
95+ }
96+
97+ #[ cfg( windows) ]
98+ fn get_env ( name : & str ) -> Result < String > {
99+ std:: env:: var ( name) . map_err ( Into :: into)
100+ }
101+
102+ #[ cfg( windows) ]
103+ fn prepend_env_path ( value : & str ) -> Result < ( ) > {
104+ let user_path = get_user_env ( "Path" ) ?. unwrap_or_default ( ) ;
105+ let new_user_path = prepend_path_value ( & user_path, value) ;
106+
107+ set_user_env ( "Path" , & new_user_path) ?;
108+
109+ let process_path = std:: env:: var ( "PATH" ) . unwrap_or_default ( ) ;
110+ std:: env:: set_var ( "PATH" , prepend_path_value ( & process_path, value) ) ;
111+
112+ Ok ( ( ) )
113+ }
114+
115+ #[ cfg( windows) ]
116+ fn get_user_env ( name : & str ) -> Result < Option < String > > {
117+ let output = std:: process:: Command :: new ( "powershell.exe" )
118+ . arg ( "-NoLogo" )
119+ . arg ( "-NoProfile" )
120+ . arg ( "-NonInteractive" )
121+ . arg ( "-Command" )
122+ . arg ( "[Environment]::GetEnvironmentVariable($args[0], 'User')" )
123+ . arg ( name)
124+ . output ( ) ?;
125+
126+ if !output. status . success ( ) {
127+ anyhow:: bail!( "Failed to read user environment variable {}" , name) ;
128+ }
129+
130+ let value = String :: from_utf8 ( output. stdout ) ?. trim ( ) . to_string ( ) ;
131+ Ok ( ( !value. is_empty ( ) ) . then_some ( value) )
132+ }
133+
134+ #[ cfg( windows) ]
135+ fn set_user_env ( name : & str , value : & str ) -> Result < ( ) > {
136+ let status = std:: process:: Command :: new ( "powershell.exe" )
137+ . arg ( "-NoLogo" )
138+ . arg ( "-NoProfile" )
139+ . arg ( "-NonInteractive" )
140+ . arg ( "-Command" )
141+ . arg ( "[Environment]::SetEnvironmentVariable($args[0], $args[1], 'User')" )
142+ . arg ( name)
143+ . arg ( value)
144+ . status ( ) ?;
145+
146+ if !status. success ( ) {
147+ anyhow:: bail!( "Failed to set user environment variable {}" , name) ;
148+ }
149+
150+ std:: env:: set_var ( name, value) ;
151+ Ok ( ( ) )
152+ }
153+
154+ #[ cfg( windows) ]
155+ fn path_contains ( path : & str , value : & str ) -> bool {
156+ path. split ( ';' ) . any ( |item| item. eq_ignore_ascii_case ( value) )
157+ }
158+
159+ #[ cfg( windows) ]
160+ fn env_path_contains ( path : & str , value : & str ) -> bool {
161+ path_contains ( path, value)
162+ }
163+
164+ #[ cfg( windows) ]
165+ fn prepend_path_value ( path : & str , value : & str ) -> String {
166+ let rest = path
167+ . split ( ';' )
168+ . filter ( |item| !item. is_empty ( ) && !item. eq_ignore_ascii_case ( value) )
169+ . collect :: < Vec < _ > > ( )
170+ . join ( ";" ) ;
171+
172+ if rest. is_empty ( ) {
173+ value. to_string ( )
174+ } else {
175+ format ! ( "{};{}" , value, rest)
176+ }
177+ }
178+
179+ #[ cfg( all( test, windows) ) ]
180+ mod tests {
181+ use super :: * ;
182+
183+ #[ test]
184+ fn prepend_path_value_moves_existing_entry_to_front ( ) {
185+ assert_eq ! (
186+ prepend_path_value(
187+ "C:\\ Windows;C:\\ Users\\ me\\ .dvm\\ bin;C:\\ Tools" ,
188+ "C:\\ Users\\ me\\ .dvm\\ bin"
189+ ) ,
190+ "C:\\ Users\\ me\\ .dvm\\ bin;C:\\ Windows;C:\\ Tools"
191+ ) ;
192+ assert_eq ! (
193+ prepend_path_value( "C:\\ Windows;C:\\ Tools" , "C:\\ Users\\ me\\ .dvm\\ bin" ) ,
194+ "C:\\ Users\\ me\\ .dvm\\ bin;C:\\ Windows;C:\\ Tools"
195+ ) ;
196+ }
197+ }
0 commit comments