@@ -626,3 +626,134 @@ fn test_structured_template_file_operations() {
626626 "mkdir -p /home/user/projects/new && touch /home/user/projects/new/file.txt.tmp"
627627 ) ;
628628}
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