@@ -289,12 +289,51 @@ where
289
289
M :: Value : AsRef < str > ,
290
290
{
291
291
if cfg ! ( windows) {
292
- Ok ( s . to_string ( ) )
292
+ substitute_variables_windows ( s , map )
293
293
} else {
294
294
subst:: substitute ( s, map) . map_err ( |err| err. to_string ( ) )
295
295
}
296
296
}
297
297
298
+ fn substitute_variables_windows < ' a , M > ( s : & str , map : & ' a M ) -> Result < String , String >
299
+ where
300
+ M : VariableMap < ' a > + ?Sized ,
301
+ M :: Value : AsRef < str > ,
302
+ {
303
+ let mut output: Vec < char > = Vec :: with_capacity ( s. len ( ) ) ;
304
+ let mut var_buf: Vec < char > = Vec :: new ( ) ;
305
+
306
+ let mut var_found = false ;
307
+
308
+ for ch in s. chars ( ) {
309
+ if ch == '%' {
310
+ if var_found {
311
+ let var_name = String :: from_iter ( var_buf) ;
312
+ var_buf = Vec :: new ( ) ;
313
+ match map. get ( & var_name) {
314
+ None => {
315
+ return Err ( format ! ( "Variable '{var_name}' not found" ) ) ;
316
+ }
317
+ Some ( value) => {
318
+ output. extend ( value. as_ref ( ) . chars ( ) ) ;
319
+ }
320
+ }
321
+ }
322
+ var_found = !var_found;
323
+ } else if !var_found {
324
+ output. push ( ch) ;
325
+ } else {
326
+ var_buf. push ( ch)
327
+ }
328
+ }
329
+
330
+ if var_found {
331
+ Err ( "Unterminated variable" . into ( ) )
332
+ } else {
333
+ Ok ( String :: from_iter ( output) )
334
+ }
335
+ }
336
+
298
337
/// Returns true if the pattern is a plain file name and not a glob pattern
299
338
fn is_literal ( pattern : & str ) -> bool {
300
339
for chr in pattern. chars ( ) {
@@ -622,6 +661,67 @@ work.files = [
622
661
assert ! ( substitute_environment_variables( "$not_unicode" , & map) . is_err( ) ) ;
623
662
}
624
663
664
+ #[ test]
665
+ fn windows_variable_names ( ) {
666
+ let mut map = HashMap :: new ( ) ;
667
+ map. insert ( "A" . to_owned ( ) , "a" . to_owned ( ) ) ;
668
+ map. insert ( "ABCD" . to_owned ( ) , "abcd" . to_owned ( ) ) ;
669
+ map. insert ( "A_0" . to_owned ( ) , "a0" . to_owned ( ) ) ;
670
+ map. insert ( "_" . to_owned ( ) , "u" . to_owned ( ) ) ;
671
+ map. insert ( "PATH" . to_owned ( ) , r#"some\path"# . to_owned ( ) ) ;
672
+
673
+ assert_eq ! ( Ok ( "" . to_owned( ) ) , substitute_variables_windows( "" , & map) ) ;
674
+ assert_eq ! (
675
+ Ok ( "test" . to_owned( ) ) ,
676
+ substitute_variables_windows( "test" , & map)
677
+ ) ;
678
+ assert_eq ! (
679
+ Ok ( "a" . to_owned( ) ) ,
680
+ substitute_variables_windows( "%A%" , & map)
681
+ ) ;
682
+ assert_eq ! (
683
+ Ok ( "abcd" . to_owned( ) ) ,
684
+ substitute_variables_windows( "%ABCD%" , & map)
685
+ ) ;
686
+ assert_eq ! (
687
+ Ok ( "a0" . to_owned( ) ) ,
688
+ substitute_variables_windows( "%A_0%" , & map)
689
+ ) ;
690
+ assert_eq ! (
691
+ Ok ( "u" . to_owned( ) ) ,
692
+ substitute_variables_windows( "%_%" , & map)
693
+ ) ;
694
+ assert_eq ! (
695
+ Ok ( r#"some\path"# . to_owned( ) ) ,
696
+ substitute_variables_windows( "%PATH%" , & map)
697
+ ) ;
698
+
699
+ // embedded in longer string
700
+ assert_eq ! (
701
+ Ok ( r#"test\a\test"# . to_owned( ) ) ,
702
+ substitute_variables_windows( r#"test\%A%\test"# , & map)
703
+ ) ;
704
+ assert_eq ! (
705
+ Ok ( r#"test\a"# . to_owned( ) ) ,
706
+ substitute_variables_windows( r#"test\%A%"# , & map)
707
+ ) ;
708
+ assert_eq ! (
709
+ Ok ( r#"a\test"# . to_owned( ) ) ,
710
+ substitute_variables_windows( r#"%A%\test"# , & map)
711
+ ) ;
712
+ assert_eq ! (
713
+ Ok ( r#"C:\test\some\path\test"# . to_owned( ) ) ,
714
+ substitute_variables_windows( r#"C:\test\%PATH%\test"# , & map)
715
+ ) ;
716
+
717
+ // error cases
718
+ assert_eq ! (
719
+ substitute_variables_windows( "%not_present%" , & map) ,
720
+ Err ( "Variable 'not_present' not found" . into( ) )
721
+ ) ;
722
+ assert ! ( substitute_variables_windows( "%not_unicode%" , & map) . is_err( ) ) ;
723
+ }
724
+
625
725
// Issue #278
626
726
#[ test]
627
727
#[ cfg( windows) ]
0 commit comments