@@ -136,11 +136,8 @@ pub(crate) enum Execution {
136136 /// Submit new execution and optionally follow its status stream until the it finishes.
137137 Submit {
138138 /// Function in the fully qualified format
139- #[ arg( value_name = "FUNCTION " ) ]
139+ #[ arg( value_name = "function " ) ]
140140 ffqn : FunctionFqn ,
141- /// Parameters encoded as an JSON
142- #[ arg( value_name = "PARAMS" ) ]
143- params : String ,
144141 /// Follow the stream of events until the execution finishes
145142 #[ arg( short, long) ]
146143 follow : bool ,
@@ -149,9 +146,21 @@ pub(crate) enum Execution {
149146 no_reconnect : bool ,
150147 /// Print output as JSON
151148 #[ arg( long) ]
152- json : bool ,
149+ json : bool , // TODO: output=json|jsonl|plain
150+ /// Parameters for the function. Accepts one of the following formats:
151+ ///
152+ /// - A single argument containing a JSON array string (e.g., '["a", "b"]')
153+ ///
154+ /// - A single argument prefixed with '@' referencing a file that contains the JSON array
155+ ///
156+ /// - Multiple individual arguments following '--' (e.g., -- "a" @secondparam.json null 1)
157+ #[ expect( clippy:: doc_link_with_quotes) ] // Intentional use of '
158+ #[ arg( name = "parameters" ) ]
159+ params : Vec < String > ,
153160 } ,
161+ /// Write a return value or an execution error to an already created stubbed execution.
154162 Stub ( Stub ) ,
163+ /// Get the current state of an execution.
155164 Get {
156165 /// Follow the status stream until the execution finishes.
157166 #[ arg( short, long) ]
@@ -169,6 +178,80 @@ pub(crate) enum Execution {
169178 } ,
170179}
171180
181+ pub ( crate ) mod params {
182+ use clap:: error:: ErrorKind ;
183+ use serde_json:: Value ;
184+
185+ pub ( crate ) fn parse_params ( params : Vec < String > ) -> Result < Vec < u8 > , clap:: Error > {
186+ if params. is_empty ( ) {
187+ Ok ( "[]" . to_string ( ) . into_bytes ( ) ) // no params, does not matter if `--` was present.
188+ } else if params. len ( ) == 1 && !dashdash ( ) {
189+ let mut params = params;
190+ let json_array = params. pop ( ) . expect ( "checked that len == 1" ) ;
191+ // Single JSON Array, or a `@`-prefixed file containing the array.
192+ let json_array = if let Some ( file_path) = json_array. strip_prefix ( '@' ) {
193+ std:: fs:: read_to_string ( file_path) . map_err ( |err| {
194+ clap:: Error :: raw (
195+ ErrorKind :: Io ,
196+ format ! (
197+ "parameter parsing failed: failed to read file '{file_path}': {err}"
198+ ) ,
199+ )
200+ } ) ?
201+ } else {
202+ json_array
203+ } ;
204+ let json_value = serde_json:: from_str ( & json_array) . map_err ( |err| {
205+ clap:: Error :: raw (
206+ ErrorKind :: ValueValidation ,
207+ format ! ( "Invalid JSON array for parameters: {err}" ) ,
208+ )
209+ } ) ?;
210+ let Value :: Array ( _) = & json_value else {
211+ return Err ( clap:: Error :: raw (
212+ ErrorKind :: ValueValidation ,
213+ "Parameter provided as JSON must be a JSON array." ,
214+ ) ) ;
215+ } ;
216+ Ok ( json_value. to_string ( ) . into_bytes ( ) )
217+ } else {
218+ // Fallback to raw arguments. Each argument is interpreted as a JSON value or a file starting with `@` that contains the JSON.
219+ let mut parsed_params: Vec < Value > = Vec :: new ( ) ;
220+ for ( idx, arg) in params. into_iter ( ) . enumerate ( ) {
221+ let arg = if let Some ( file_path) = arg. strip_prefix ( '@' ) {
222+ std:: fs:: read_to_string ( file_path) . map_err ( |err| {
223+ clap:: Error :: raw (
224+ ErrorKind :: Io ,
225+ format ! (
226+ "{idx}-th parameter parsing failed: failed to read file '{file_path}': {err}"
227+ ) ,
228+ )
229+ } ) ?
230+ } else {
231+ arg
232+ } ;
233+ let json = serde_json:: from_str ( & arg) . map_err ( |err| {
234+ clap:: Error :: raw (
235+ ErrorKind :: ValueValidation ,
236+ format ! ( "{idx}-th parameter parsing failed: cannot parse as JSON: {err}" ) ,
237+ )
238+ } ) ?;
239+ parsed_params. push ( json) ;
240+ }
241+ Ok ( Value :: Array ( parsed_params) . to_string ( ) . into_bytes ( ) )
242+ }
243+ }
244+
245+ fn dashdash ( ) -> bool {
246+ // Ambigous: Either the single JSON Array representing all parameters,
247+ // OR `-- "first-and-only-json-param"`
248+ let mut rev_arg_iter = std:: env:: args ( ) . rev ( ) ;
249+ rev_arg_iter. next ( ) . expect ( "last arg must be present" ) ;
250+ let maybe_separator = rev_arg_iter. next ( ) . expect ( "last-1 arg must be present" ) ;
251+ maybe_separator == "--"
252+ }
253+ }
254+
172255#[ derive( Debug , clap:: Args ) ]
173256#[ command( group(
174257 ArgGroup :: new( "result" )
0 commit comments