@@ -213,10 +213,15 @@ impl Builtin for Jq {
213213 // --argjson name value: bind $name to parsed JSON value
214214 if i + 2 < ctx. args . len ( ) {
215215 let name = format ! ( "${}" , & ctx. args[ i + 1 ] ) ;
216- let json_val: serde_json:: Value = serde_json:: from_str ( & ctx. args [ i + 2 ] )
217- . map_err ( |e| {
218- Error :: Execution ( format ! ( "jq: invalid JSON for --argjson: {}" , e) )
219- } ) ?;
216+ let json_val: serde_json:: Value = match serde_json:: from_str ( & ctx. args [ i + 2 ] ) {
217+ Ok ( v) => v,
218+ Err ( e) => {
219+ return Ok ( ExecResult :: err (
220+ format ! ( "jq: invalid JSON for --argjson: {}\n " , e) ,
221+ 2 ,
222+ ) ) ;
223+ }
224+ } ;
220225 var_bindings. push ( ( name, json_val) ) ;
221226 i += 3 ;
222227 continue ;
@@ -298,15 +303,19 @@ impl Builtin for Jq {
298303 path : ( ) ,
299304 } ;
300305
301- let modules = loader. load ( & arena, program) . map_err ( |errs| {
302- Error :: Execution ( format ! (
303- "jq: parse error: {}" ,
304- errs. into_iter( )
305- . map( |e| format!( "{:?}" , e) )
306- . collect:: <Vec <_>>( )
307- . join( ", " )
308- ) )
309- } ) ?;
306+ let modules = match loader. load ( & arena, program) {
307+ Ok ( m) => m,
308+ Err ( errs) => {
309+ let msg = format ! (
310+ "jq: parse error: {}\n " ,
311+ errs. into_iter( )
312+ . map( |e| format!( "{:?}" , e) )
313+ . collect:: <Vec <_>>( )
314+ . join( ", " )
315+ ) ;
316+ return Ok ( ExecResult :: err ( msg, 3 ) ) ;
317+ }
318+ } ;
310319
311320 // Compile the filter, registering any --arg/--argjson variable names
312321 let var_names: Vec < & str > = var_bindings. iter ( ) . map ( |( n, _) | n. as_str ( ) ) . collect ( ) ;
@@ -316,15 +325,19 @@ impl Builtin for Jq {
316325 } else {
317326 compiler. with_global_vars ( var_names. iter ( ) . copied ( ) )
318327 } ;
319- let filter = compiler. compile ( modules) . map_err ( |errs| {
320- Error :: Execution ( format ! (
321- "jq: compile error: {}" ,
322- errs. into_iter( )
323- . map( |e| format!( "{:?}" , e) )
324- . collect:: <Vec <_>>( )
325- . join( ", " )
326- ) )
327- } ) ?;
328+ let filter = match compiler. compile ( modules) {
329+ Ok ( f) => f,
330+ Err ( errs) => {
331+ let msg = format ! (
332+ "jq: compile error: {}\n " ,
333+ errs. into_iter( )
334+ . map( |e| format!( "{:?}" , e) )
335+ . collect:: <Vec <_>>( )
336+ . join( ", " )
337+ ) ;
338+ return Ok ( ExecResult :: err ( msg, 3 ) ) ;
339+ }
340+ } ;
328341
329342 // Process input as JSON
330343 let mut output = String :: new ( ) ;
@@ -335,12 +348,16 @@ impl Builtin for Jq {
335348 vec ! [ Val :: from( serde_json:: Value :: Null ) ]
336349 } else if slurp {
337350 // -s flag: read all inputs into a single array
338- let vals = Self :: parse_json_values ( input) ?;
339- vec ! [ Val :: from( serde_json:: Value :: Array ( vals) ) ]
351+ match Self :: parse_json_values ( input) {
352+ Ok ( vals) => vec ! [ Val :: from( serde_json:: Value :: Array ( vals) ) ] ,
353+ Err ( e) => return Ok ( ExecResult :: err ( format ! ( "{}\n " , e) , 5 ) ) ,
354+ }
340355 } else {
341356 // Parse all JSON values from input (handles multi-line and NDJSON)
342- let json_vals = Self :: parse_json_values ( input) ?;
343- json_vals. into_iter ( ) . map ( Val :: from) . collect ( )
357+ match Self :: parse_json_values ( input) {
358+ Ok ( json_vals) => json_vals. into_iter ( ) . map ( Val :: from) . collect ( ) ,
359+ Err ( e) => return Ok ( ExecResult :: err ( format ! ( "{}\n " , e) , 5 ) ) ,
360+ }
344361 } ;
345362
346363 // Expose bashkit's shell env/variables to the process environment so
@@ -368,8 +385,12 @@ impl Builtin for Jq {
368385 let shared_inputs = RcIter :: new ( inputs_to_process. into_iter ( ) . map ( Ok :: < Val , String > ) ) ;
369386
370387 for jaq_input in & shared_inputs {
371- let jaq_input: Val =
372- jaq_input. map_err ( |e| Error :: Execution ( format ! ( "jq: input error: {}" , e) ) ) ?;
388+ let jaq_input: Val = match jaq_input {
389+ Ok ( v) => v,
390+ Err ( e) => {
391+ return Ok ( ExecResult :: err ( format ! ( "jq: input error: {}\n " , e) , 5 ) ) ;
392+ }
393+ } ;
373394
374395 // Run the filter, passing any --arg/--argjson variable values
375396 let var_vals: Vec < Val > = var_bindings
@@ -432,10 +453,10 @@ impl Builtin for Jq {
432453 }
433454 }
434455 Err ( e) => {
435- return Err ( Error :: Execution ( format ! (
436- "jq: output error: {}" ,
437- e
438- ) ) ) ;
456+ return Ok ( ExecResult :: err (
457+ format ! ( "jq: output error: {}\n " , e ) ,
458+ 5 ,
459+ ) ) ;
439460 }
440461 }
441462 }
@@ -447,10 +468,10 @@ impl Builtin for Jq {
447468 output. push ( '\n' ) ;
448469 }
449470 Err ( e) => {
450- return Err ( Error :: Execution ( format ! (
451- "jq: output error: {}" ,
452- e
453- ) ) ) ;
471+ return Ok ( ExecResult :: err (
472+ format ! ( "jq: output error: {}\n " , e ) ,
473+ 5 ,
474+ ) ) ;
454475 }
455476 }
456477 } else if tab_indent {
@@ -472,16 +493,16 @@ impl Builtin for Jq {
472493 output. push ( '\n' ) ;
473494 }
474495 Err ( e) => {
475- return Err ( Error :: Execution ( format ! (
476- "jq: output error: {}" ,
477- e
478- ) ) ) ;
496+ return Ok ( ExecResult :: err (
497+ format ! ( "jq: output error: {}\n " , e ) ,
498+ 5 ,
499+ ) ) ;
479500 }
480501 }
481502 }
482503 }
483504 Err ( e) => {
484- return Err ( Error :: Execution ( format ! ( "jq: runtime error: {:?}" , e) ) ) ;
505+ return Ok ( ExecResult :: err ( format ! ( "jq: runtime error: {:?}\n " , e) , 5 ) ) ;
485506 }
486507 }
487508 }
@@ -598,6 +619,52 @@ mod tests {
598619 Ok ( result. stdout )
599620 }
600621
622+ async fn run_jq_result ( filter : & str , input : & str ) -> Result < ExecResult > {
623+ let jq = Jq ;
624+ let fs = Arc :: new ( InMemoryFs :: new ( ) ) ;
625+ let mut vars = HashMap :: new ( ) ;
626+ let mut cwd = PathBuf :: from ( "/" ) ;
627+ let args = vec ! [ filter. to_string( ) ] ;
628+
629+ let ctx = Context {
630+ args : & args,
631+ env : & HashMap :: new ( ) ,
632+ variables : & mut vars,
633+ cwd : & mut cwd,
634+ fs,
635+ stdin : Some ( input) ,
636+ #[ cfg( feature = "http_client" ) ]
637+ http_client : None ,
638+ #[ cfg( feature = "git" ) ]
639+ git_client : None ,
640+ } ;
641+
642+ jq. execute ( ctx) . await
643+ }
644+
645+ async fn run_jq_result_with_args ( args : & [ & str ] , input : & str ) -> Result < ExecResult > {
646+ let jq = Jq ;
647+ let fs = Arc :: new ( InMemoryFs :: new ( ) ) ;
648+ let mut vars = HashMap :: new ( ) ;
649+ let mut cwd = PathBuf :: from ( "/" ) ;
650+ let args: Vec < String > = args. iter ( ) . map ( |s| s. to_string ( ) ) . collect ( ) ;
651+
652+ let ctx = Context {
653+ args : & args,
654+ env : & HashMap :: new ( ) ,
655+ variables : & mut vars,
656+ cwd : & mut cwd,
657+ fs,
658+ stdin : Some ( input) ,
659+ #[ cfg( feature = "http_client" ) ]
660+ http_client : None ,
661+ #[ cfg( feature = "git" ) ]
662+ git_client : None ,
663+ } ;
664+
665+ jq. execute ( ctx) . await
666+ }
667+
601668 #[ tokio:: test]
602669 async fn test_jq_raw_output ( ) {
603670 let result = run_jq_with_args ( & [ "-r" , ".name" ] , r#"{"name":"test"}"# )
@@ -658,15 +725,15 @@ mod tests {
658725 let close = "]" . repeat ( depth) ;
659726 let input = format ! ( "{open}1{close}" ) ;
660727
661- let result = run_jq ( "." , & input) . await ;
728+ let result = run_jq_result ( "." , & input) . await . unwrap ( ) ;
662729 assert ! (
663- result. is_err ( ) ,
730+ result. exit_code != 0 ,
664731 "deeply nested JSON arrays must be rejected"
665732 ) ;
666- let err = result. unwrap_err ( ) . to_string ( ) ;
667733 assert ! (
668- err. contains( "nesting too deep" ) || err. contains( "recursion limit" ) ,
669- "error should mention nesting or recursion limit: {err}"
734+ result. stderr. contains( "nesting too deep" ) || result. stderr. contains( "recursion limit" ) ,
735+ "error should mention nesting or recursion limit: {}" ,
736+ result. stderr
670737 ) ;
671738 }
672739
@@ -680,15 +747,15 @@ mod tests {
680747 input = format ! ( r#"{{"a":{input}}}"# ) ;
681748 }
682749
683- let result = run_jq ( "." , & input) . await ;
750+ let result = run_jq_result ( "." , & input) . await . unwrap ( ) ;
684751 assert ! (
685- result. is_err ( ) ,
752+ result. exit_code != 0 ,
686753 "deeply nested JSON objects must be rejected"
687754 ) ;
688- let err = result. unwrap_err ( ) . to_string ( ) ;
689755 assert ! (
690- err. contains( "nesting too deep" ) || err. contains( "recursion limit" ) ,
691- "error should mention nesting or recursion limit: {err}"
756+ result. stderr. contains( "nesting too deep" ) || result. stderr. contains( "recursion limit" ) ,
757+ "error should mention nesting or recursion limit: {}" ,
758+ result. stderr
692759 ) ;
693760 }
694761
@@ -962,22 +1029,38 @@ mod tests {
9621029
9631030 #[ tokio:: test]
9641031 async fn test_jq_invalid_json_input ( ) {
965- let result = run_jq ( "." , "not valid json" ) ;
966- assert ! ( result. await . is_err( ) , "invalid JSON input should error" ) ;
1032+ let result = run_jq_result ( "." , "not valid json" ) . await . unwrap ( ) ;
1033+ assert ! (
1034+ result. exit_code != 0 ,
1035+ "invalid JSON input should have non-zero exit"
1036+ ) ;
1037+ assert ! (
1038+ result. stderr. contains( "jq:" ) ,
1039+ "should have jq error in stderr"
1040+ ) ;
9671041 }
9681042
9691043 #[ tokio:: test]
9701044 async fn test_jq_invalid_filter_syntax ( ) {
971- let result = run_jq ( ".[" , r#"{"a":1}"# ) ;
972- assert ! ( result. await . is_err( ) , "invalid filter should error" ) ;
1045+ let result = run_jq_result ( ".[" , r#"{"a":1}"# ) . await . unwrap ( ) ;
1046+ assert ! (
1047+ result. exit_code != 0 ,
1048+ "invalid filter should have non-zero exit"
1049+ ) ;
1050+ assert ! (
1051+ result. stderr. contains( "jq:" ) ,
1052+ "should have jq error in stderr"
1053+ ) ;
9731054 }
9741055
9751056 #[ tokio:: test]
9761057 async fn test_jq_invalid_argjson_value ( ) {
977- let result = run_jq_with_args ( & [ "--argjson" , "x" , "not json" , "-n" , "$x" ] , "" ) ;
1058+ let result = run_jq_result_with_args ( & [ "--argjson" , "x" , "not json" , "-n" , "$x" ] , "" )
1059+ . await
1060+ . unwrap ( ) ;
9781061 assert ! (
979- result. await . is_err ( ) ,
980- "invalid JSON for --argjson should error "
1062+ result. exit_code != 0 ,
1063+ "invalid JSON for --argjson should have non-zero exit "
9811064 ) ;
9821065 }
9831066
0 commit comments