@@ -626,3 +626,134 @@ fn test_structured_template_file_operations() {
626
626
"mkdir -p /home/user/projects/new && touch /home/user/projects/new/file.txt.tmp"
627
627
) ;
628
628
}
629
+
630
+ // Tests for shell variable support (${...} patterns)
631
+
632
+ #[ test]
633
+ fn test_multi_template_shell_variable_basic ( ) {
634
+ // Test basic shell variable pattern ${VAR}
635
+ let template = MultiTemplate :: parse ( "${HOME}/projects/{upper}" ) . unwrap ( ) ;
636
+ let result = template. format ( "readme" ) . unwrap ( ) ;
637
+ assert_eq ! ( result, "${HOME}/projects/README" ) ;
638
+ }
639
+
640
+ #[ test]
641
+ fn test_multi_template_shell_variable_with_default ( ) {
642
+ // Test shell variable with default value ${VAR:-default}
643
+ let template = MultiTemplate :: parse ( "${EDITOR:-vim} {upper}.txt" ) . unwrap ( ) ;
644
+ let result = template. format ( "config" ) . unwrap ( ) ;
645
+ assert_eq ! ( result, "${EDITOR:-vim} CONFIG.txt" ) ;
646
+ }
647
+
648
+ #[ test]
649
+ fn test_multi_template_shell_variable_specific_case ( ) {
650
+ // Test the specific case that was failing: ${EDITOR:-vim} {}
651
+ let template = MultiTemplate :: parse ( "${EDITOR:-vim} {}" ) . unwrap ( ) ;
652
+ let result = template. format ( "file.txt" ) . unwrap ( ) ;
653
+ assert_eq ! ( result, "${EDITOR:-vim} file.txt" ) ;
654
+ }
655
+
656
+ #[ test]
657
+ fn test_multi_template_multiple_shell_variables ( ) {
658
+ // Test multiple shell variables in one template
659
+ let template = MultiTemplate :: parse ( "${USER}@${HOST}: {upper}" ) . unwrap ( ) ;
660
+ let result = template. format ( "hello world" ) . unwrap ( ) ;
661
+ assert_eq ! ( result, "${USER}@${HOST}: HELLO WORLD" ) ;
662
+ }
663
+
664
+ #[ test]
665
+ fn test_multi_template_shell_variable_complex ( ) {
666
+ // Test complex shell variable expressions
667
+ let template = MultiTemplate :: parse ( "${PATH:+/usr/bin:}${HOME}/bin {lower}" ) . unwrap ( ) ;
668
+ let result = template. format ( "SCRIPT" ) . unwrap ( ) ;
669
+ assert_eq ! ( result, "${PATH:+/usr/bin:}${HOME}/bin script" ) ;
670
+ }
671
+
672
+ #[ test]
673
+ fn test_multi_template_shell_variable_empty ( ) {
674
+ // Test empty shell variable ${}
675
+ let template = MultiTemplate :: parse ( "${} prefix {upper}" ) . unwrap ( ) ;
676
+ let result = template. format ( "test" ) . unwrap ( ) ;
677
+ assert_eq ! ( result, "${} prefix TEST" ) ;
678
+ }
679
+
680
+ #[ test]
681
+ fn test_multi_template_shell_variable_nested_braces ( ) {
682
+ // Test shell variables with nested braces
683
+ let template = MultiTemplate :: parse ( "${CONFIG_DIR:-${HOME}/.config} {lower}" ) . unwrap ( ) ;
684
+ let result = template. format ( "APP" ) . unwrap ( ) ;
685
+ assert_eq ! ( result, "${CONFIG_DIR:-${HOME}/.config} app" ) ;
686
+ }
687
+
688
+ #[ test]
689
+ fn test_multi_template_shell_variable_mixed_with_templates ( ) {
690
+ // Test mixing shell variables with multiple template sections
691
+ let template = MultiTemplate :: parse ( "cp {upper} ${BACKUP_DIR:-/backup}/{lower}.bak" ) . unwrap ( ) ;
692
+ let result = template. format ( "important.txt" ) . unwrap ( ) ;
693
+ assert_eq ! (
694
+ result,
695
+ "cp IMPORTANT.TXT ${BACKUP_DIR:-/backup}/important.txt.bak"
696
+ ) ;
697
+ }
698
+
699
+ #[ test]
700
+ fn test_multi_template_shell_variable_at_boundaries ( ) {
701
+ // Test shell variables at start/end of template
702
+ let template = MultiTemplate :: parse ( "${PREFIX} middle {upper} ${SUFFIX}" ) . unwrap ( ) ;
703
+ let result = template. format ( "test" ) . unwrap ( ) ;
704
+ assert_eq ! ( result, "${PREFIX} middle TEST ${SUFFIX}" ) ;
705
+ }
706
+
707
+ #[ test]
708
+ fn test_multi_template_shell_variable_consecutive ( ) {
709
+ // Test consecutive shell variables
710
+ let template = MultiTemplate :: parse ( "${VAR1}${VAR2} {upper}" ) . unwrap ( ) ;
711
+ let result = template. format ( "hello" ) . unwrap ( ) ;
712
+ assert_eq ! ( result, "${VAR1}${VAR2} HELLO" ) ;
713
+ }
714
+
715
+ #[ test]
716
+ fn test_multi_template_shell_variable_special_characters ( ) {
717
+ // Test shell variables with special characters
718
+ let template = MultiTemplate :: parse ( "${HOME}/some-dir/sub_dir {upper}" ) . unwrap ( ) ;
719
+ let result = template. format ( "file name" ) . unwrap ( ) ;
720
+ assert_eq ! ( result, "${HOME}/some-dir/sub_dir FILE NAME" ) ;
721
+ }
722
+
723
+ #[ test]
724
+ fn test_multi_template_shell_variable_real_world_example ( ) {
725
+ // Test real-world shell command example
726
+ let template = MultiTemplate :: parse ( "${EDITOR:-nano} ${HOME}/.config/{lower}.conf" ) . unwrap ( ) ;
727
+ let result = template. format ( "MYAPP" ) . unwrap ( ) ;
728
+ assert_eq ! ( result, "${EDITOR:-nano} ${HOME}/.config/myapp.conf" ) ;
729
+ }
730
+
731
+ // Error handling tests for shell variables
732
+
733
+ #[ test]
734
+ fn test_multi_template_unclosed_shell_variable_error ( ) {
735
+ // Test error when shell variable is not closed
736
+ let result = MultiTemplate :: parse ( "${HOME unclosed {upper}" ) ;
737
+ assert ! ( result. is_err( ) ) ;
738
+ assert ! (
739
+ result
740
+ . unwrap_err( )
741
+ . contains( "Unclosed shell variable brace" )
742
+ ) ;
743
+ }
744
+
745
+ #[ test]
746
+ fn test_multi_template_shell_variable_complex_nesting ( ) {
747
+ // Test complex nesting of shell variables and templates
748
+ let template = MultiTemplate :: parse (
749
+ "${DIR:-${HOME}/default} contains {split:,:..|filter:\\ .txt$|join: and }" ,
750
+ )
751
+ . unwrap ( ) ;
752
+ let result = template
753
+ . format ( "file1.txt,doc.pdf,file2.txt,readme.md" )
754
+ . unwrap ( ) ;
755
+ assert_eq ! (
756
+ result,
757
+ "${DIR:-${HOME}/default} contains file1.txt and file2.txt"
758
+ ) ;
759
+ }
0 commit comments