@@ -282,8 +282,12 @@ fn apply_range<T: Clone>(items: &[T], range: &RangeSpec) -> Vec<T> {
282282 let len = items. len ( ) ;
283283 match range {
284284 RangeSpec :: Index ( idx) => {
285+ // Handle empty collections
286+ if len == 0 {
287+ return vec ! [ ] ;
288+ }
285289 let mut i = resolve_index ( * idx, len) ;
286- if i >= len && len > 0 {
290+ if i >= len {
287291 i = len - 1 ;
288292 }
289293 items. get ( i) . cloned ( ) . map_or ( vec ! [ ] , |v| vec ! [ v] )
@@ -429,6 +433,9 @@ fn apply_ops(input: &str, ops: &[StringOp]) -> Result<String, String> {
429433 } ,
430434 }
431435 }
436+
437+ // Note: If the final value is a List, we join using the last split separator
438+ // or a space if no split operation was performed
432439 Ok ( match val {
433440 Value :: Str ( s) => s,
434441 Value :: List ( list) => list. join ( last_split_sep. as_deref ( ) . unwrap_or ( " " ) ) ,
@@ -769,18 +776,21 @@ mod tests {
769776 ) ;
770777 }
771778
779+ // New edge case tests
772780 #[ test]
773781 fn test_empty_operations ( ) {
774- // Empty template should be handled
775- assert ! ( process( "test" , "{}" ) . is_ok ( ) ) ;
782+ // Empty template should return the input as-is
783+ assert_eq ! ( process( "test" , "{}" ) . unwrap ( ) , "test" ) ;
776784 }
777785
778786 #[ test]
779787 fn test_invalid_range_edge_cases ( ) {
780788 // Test what happens with very large indices
781789 assert_eq ! ( process( "a,b,c" , "{split:,:100}" ) . unwrap( ) , "c" ) ;
782- // Test empty range
790+ // Test empty range (start > end)
783791 assert_eq ! ( process( "a,b,c" , "{split:,:3..1}" ) . unwrap( ) , "" ) ;
792+ // Test range that starts beyond bounds
793+ assert_eq ! ( process( "a,b,c" , "{split:,:10..20}" ) . unwrap( ) , "" ) ;
784794 }
785795
786796 #[ test]
@@ -795,16 +805,98 @@ mod tests {
795805 assert_eq ! ( process( "hello" , "{strip:}" ) . unwrap( ) , "hello" ) ;
796806 }
797807
808+ #[ test]
809+ fn test_slice_empty_string ( ) {
810+ assert_eq ! ( process( "" , "{slice:0}" ) . unwrap( ) , "" ) ;
811+ assert_eq ! ( process( "" , "{slice:-1}" ) . unwrap( ) , "" ) ;
812+ assert_eq ! ( process( "" , "{slice:1..3}" ) . unwrap( ) , "" ) ;
813+ assert_eq ! ( process( "" , "{slice:..}" ) . unwrap( ) , "" ) ;
814+ }
815+
816+ #[ test]
817+ fn test_slice_empty_list ( ) {
818+ // Split an empty string creates empty list
819+ assert_eq ! ( process( "" , "{split:,:..:slice:0}" ) . unwrap( ) , "" ) ;
820+ assert_eq ! ( process( "" , "{split:,:..:slice:1..3}" ) . unwrap( ) , "" ) ;
821+ }
822+
798823 #[ test]
799824 fn test_invalid_regex ( ) {
800825 // Should handle invalid regex gracefully
801826 assert ! ( process( "test" , "{replace:s/[/replacement/}" ) . is_err( ) ) ;
827+ assert ! ( process( "test" , "{replace:s/*/replacement/}" ) . is_err( ) ) ;
802828 }
803829
804830 #[ test]
805- fn test_slice_empty_string ( ) {
806- assert_eq ! ( process( "" , "{slice:0}" ) . unwrap( ) , "" ) ;
807- assert_eq ! ( process( "" , "{slice:-1}" ) . unwrap( ) , "" ) ;
831+ fn test_malformed_sed_strings ( ) {
832+ // Missing closing slash
833+ assert ! ( process( "test" , "{replace:s/pattern/replacement}" ) . is_err( ) ) ;
834+ // No pattern
835+ assert ! ( process( "test" , "{replace:s//replacement/}" ) . is_err( ) ) ;
836+ // Wrong format entirely
837+ assert ! ( process( "test" , "{replace:pattern/replacement}" ) . is_err( ) ) ;
838+ }
839+
840+ #[ test]
841+ fn test_invalid_template_format ( ) {
842+ // Missing braces
843+ assert ! ( process( "test" , "split:,:0" ) . is_err( ) ) ;
844+ // Missing opening brace
845+ assert ! ( process( "test" , "split:,:0}" ) . is_err( ) ) ;
846+ // Missing closing brace
847+ assert ! ( process( "test" , "{split:,:0" ) . is_err( ) ) ;
848+ }
849+
850+ #[ test]
851+ fn test_unknown_operation ( ) {
852+ assert ! ( process( "test" , "{unknown}" ) . is_err( ) ) ;
853+ assert ! ( process( "test" , "{badop:arg}" ) . is_err( ) ) ;
854+ }
855+
856+ #[ test]
857+ fn test_invalid_range_strings ( ) {
858+ // Invalid range formats
859+ assert ! ( process( "a,b,c" , "{split:,:abc}" ) . is_err( ) ) ;
860+ assert ! ( process( "a,b,c" , "{split:,:1..abc}" ) . is_err( ) ) ;
861+ assert ! ( process( "hello" , "{slice:xyz}" ) . is_err( ) ) ;
862+ }
863+
864+ #[ test]
865+ fn test_large_indices_handling ( ) {
866+ let input = "a,b,c" ;
867+ // Very large positive index should clamp to last element
868+ assert_eq ! ( process( input, "{split:,:999999}" ) . unwrap( ) , "c" ) ;
869+ // Very large negative index should clamp to first element
870+ assert_eq ! ( process( input, "{split:,:-999999}" ) . unwrap( ) , "a" ) ;
871+ }
872+
873+ #[ test]
874+ fn test_operations_on_empty_list ( ) {
875+ // Create empty list and apply operations
876+ let input = "" ;
877+ assert_eq ! ( process( input, "{split:,:..:upper}" ) . unwrap( ) , "" ) ;
878+ assert_eq ! ( process( input, "{split:,:..:lower}" ) . unwrap( ) , "" ) ;
879+ assert_eq ! ( process( input, "{split:,:..:trim}" ) . unwrap( ) , "" ) ;
880+ assert_eq ! ( process( input, "{split:,:..:append:!}" ) . unwrap( ) , "!" ) ;
881+ assert_eq ! ( process( input, "{split:,:..:prepend:_}" ) . unwrap( ) , "_" ) ;
882+ }
883+
884+ #[ test]
885+ fn test_final_output_behavior ( ) {
886+ // Test documented behavior: List joins with last split separator or space
887+ let input = "a,b,c" ;
888+
889+ // With split operation - should use comma
890+ assert_eq ! ( process( input, "{split:,:..:upper}" ) . unwrap( ) , "A,B,C" ) ;
891+
892+ // Without split operation - should use space (no split occurred)
893+ assert_eq ! ( process( "hello world" , "{upper}" ) . unwrap( ) , "HELLO WORLD" ) ;
894+
895+ // Multiple splits - should use last split separator
896+ assert_eq ! (
897+ process( input, "{split:,:..:join:-:split:-:..:upper}" ) . unwrap( ) ,
898+ "A-B-C"
899+ ) ;
808900 }
809901}
810902
0 commit comments