@@ -11,14 +11,14 @@ use tracing::debug;
1111#[ derive( Debug , Default ) ]
1212pub struct Join { }
1313
14- fn stringify_value ( v : & Value ) -> String {
14+ fn stringify_value ( v : & Value ) -> Result < String , DscError > {
1515 match v {
16- Value :: String ( s) => s. clone ( ) ,
17- Value :: Number ( n) => n. to_string ( ) ,
18- Value :: Bool ( b) => b. to_string ( ) ,
19- Value :: Null => " null". to_string ( ) ,
20- // Fallback to JSON for arrays/objects
21- _ => serde_json :: to_string ( v ) . unwrap_or_default ( ) ,
16+ Value :: String ( s) => Ok ( s. clone ( ) ) ,
17+ Value :: Number ( n) => Ok ( n. to_string ( ) ) ,
18+ Value :: Bool ( b) => Ok ( b. to_string ( ) ) ,
19+ Value :: Null => Err ( DscError :: Parser ( "Array elements cannot be null". to_string ( ) ) ) ,
20+ Value :: Array ( _ ) => Err ( DscError :: Parser ( "Array elements cannot be arrays" . to_string ( ) ) ) ,
21+ Value :: Object ( _ ) => Err ( DscError :: Parser ( "Array elements cannot be objects" . to_string ( ) ) ) ,
2222 }
2323}
2424
@@ -32,15 +32,7 @@ impl Function for Join {
3232 max_args : 2 ,
3333 accepted_arg_ordered_types : vec ! [
3434 vec![ FunctionArgKind :: Array ] ,
35- // delimiter: accept any type (no validation), convert to string
36- vec![
37- FunctionArgKind :: Array ,
38- FunctionArgKind :: Boolean ,
39- FunctionArgKind :: Null ,
40- FunctionArgKind :: Number ,
41- FunctionArgKind :: Object ,
42- FunctionArgKind :: String ,
43- ] ,
35+ vec![ FunctionArgKind :: String ] ,
4436 ] ,
4537 remaining_arg_accepted_types : None ,
4638 return_types : vec ! [ FunctionArgKind :: String ] ,
@@ -50,11 +42,12 @@ impl Function for Join {
5042 fn invoke ( & self , args : & [ Value ] , _context : & Context ) -> Result < Value , DscError > {
5143 debug ! ( "{}" , t!( "functions.join.invoked" ) ) ;
5244
53- let delimiter = stringify_value ( & args[ 1 ] ) ;
45+ let delimiter = args[ 1 ] . as_str ( ) . unwrap ( ) ;
5446
5547 if let Some ( array) = args[ 0 ] . as_array ( ) {
56- let items: Vec < String > = array. iter ( ) . map ( stringify_value) . collect ( ) ;
57- return Ok ( Value :: String ( items. join ( & delimiter) ) ) ;
48+ let items: Result < Vec < String > , DscError > = array. iter ( ) . map ( stringify_value) . collect ( ) ;
49+ let items = items?;
50+ return Ok ( Value :: String ( items. join ( delimiter) ) ) ;
5851 }
5952
6053 Err ( DscError :: Parser ( t ! ( "functions.join.invalidArrayArg" ) . to_string ( ) ) )
@@ -65,6 +58,8 @@ impl Function for Join {
6558mod tests {
6659 use crate :: configure:: context:: Context ;
6760 use crate :: parser:: Statement ;
61+ use super :: Join ;
62+ use crate :: functions:: Function ;
6863
6964 #[ test]
7065 fn join_array_of_strings ( ) {
@@ -86,4 +81,48 @@ mod tests {
8681 let result = parser. parse_and_execute ( "[join(createArray(1,2,3), ',')]" , & Context :: new ( ) ) . unwrap ( ) ;
8782 assert_eq ! ( result, "1,2,3" ) ;
8883 }
84+
85+ #[ test]
86+ fn join_array_with_null_fails ( ) {
87+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
88+ let result = parser. parse_and_execute ( "[join(createArray('a', null()), ',')]" , & Context :: new ( ) ) ;
89+ assert ! ( result. is_err( ) ) ;
90+ // The error comes from argument validation, not our function
91+ assert ! ( result. unwrap_err( ) . to_string( ) . contains( "does not accept null arguments" ) ) ;
92+ }
93+
94+ #[ test]
95+ fn join_array_with_array_fails ( ) {
96+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
97+ let result = parser. parse_and_execute ( "[join(createArray('a', createArray('b')), ',')]" , & Context :: new ( ) ) ;
98+ assert ! ( result. is_err( ) ) ;
99+ let error_msg = result. unwrap_err ( ) . to_string ( ) ;
100+ assert ! ( error_msg. contains( "Arguments must all be arrays" ) || error_msg. contains( "mixed types" ) ) ;
101+ }
102+
103+ #[ test]
104+ fn join_array_with_object_fails ( ) {
105+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
106+ let result = parser. parse_and_execute ( "[join(createArray('a', createObject('key', 'value')), ',')]" , & Context :: new ( ) ) ;
107+ assert ! ( result. is_err( ) ) ;
108+ let error_msg = result. unwrap_err ( ) . to_string ( ) ;
109+ assert ! ( error_msg. contains( "Arguments must all be" ) || error_msg. contains( "mixed types" ) ) ;
110+ }
111+
112+ #[ test]
113+ fn join_direct_test_with_mixed_array ( ) {
114+ use serde_json:: json;
115+ use crate :: configure:: context:: Context ;
116+
117+ let join_fn = Join :: default ( ) ;
118+ let args = vec ! [
119+ json!( [ "hello" , { "key" : "value" } ] ) , // Array with string and object
120+ json!( "," )
121+ ] ;
122+ let result = join_fn. invoke ( & args, & Context :: new ( ) ) ;
123+
124+ assert ! ( result. is_err( ) ) ;
125+ let error_msg = result. unwrap_err ( ) . to_string ( ) ;
126+ assert ! ( error_msg. contains( "Array elements cannot be objects" ) ) ;
127+ }
89128}
0 commit comments