77//! jq '.[] | .id' < data.json
88
99use async_trait:: async_trait;
10- use jaq_core:: { Compiler , Ctx , RcIter , load} ;
10+ use jaq_core:: load:: { Arena , File , Loader } ;
11+ use jaq_core:: { Compiler , Ctx , Vars , data} ;
1112use jaq_json:: Val ;
13+ use jaq_std:: input:: { HasInputs , Inputs , RcIter } ;
1214
1315use super :: { Builtin , Context , read_text_file, resolve_path} ;
1416use crate :: error:: { Error , Result } ;
1517use crate :: interpreter:: ExecResult ;
1618
19+ /// Custom DataT that holds both the LUT and a shared input iterator.
20+ /// Required by jaq 3.0 for `input`/`inputs` filter support.
21+ struct InputData < V > ( std:: marker:: PhantomData < V > ) ;
22+
23+ impl < V : jaq_core:: ValT + ' static > data:: DataT for InputData < V > {
24+ type V < ' a > = V ;
25+ type Data < ' a > = InputDataRef < ' a , V > ;
26+ }
27+
28+ #[ derive( Clone ) ]
29+ struct InputDataRef < ' a , V : jaq_core:: ValT + ' static > {
30+ lut : & ' a jaq_core:: Lut < InputData < V > > ,
31+ inputs : & ' a RcIter < dyn Iterator < Item = std:: result:: Result < V , String > > + ' a > ,
32+ }
33+
34+ impl < ' a , V : jaq_core:: ValT + ' static > data:: HasLut < ' a , InputData < V > > for InputDataRef < ' a , V > {
35+ fn lut ( & self ) -> & ' a jaq_core:: Lut < InputData < V > > {
36+ self . lut
37+ }
38+ }
39+
40+ impl < ' a , V : jaq_core:: ValT + ' static > HasInputs < ' a , V > for InputDataRef < ' a , V > {
41+ fn inputs ( & self ) -> Inputs < ' a , V > {
42+ self . inputs
43+ }
44+ }
45+
46+ /// Convert serde_json::Value to jaq Val.
47+ fn serde_to_val ( v : serde_json:: Value ) -> Val {
48+ match v {
49+ serde_json:: Value :: Null => Val :: Null ,
50+ serde_json:: Value :: Bool ( b) => Val :: from ( b) ,
51+ serde_json:: Value :: Number ( n) => {
52+ if let Some ( i) = n. as_i64 ( ) {
53+ if let Ok ( i) = isize:: try_from ( i) {
54+ Val :: from ( i)
55+ } else {
56+ Val :: from ( i as f64 )
57+ }
58+ } else if let Some ( f) = n. as_f64 ( ) {
59+ Val :: from ( f)
60+ } else {
61+ Val :: from ( 0isize ) // unreachable in practice
62+ }
63+ }
64+ serde_json:: Value :: String ( s) => Val :: from ( s) ,
65+ serde_json:: Value :: Array ( arr) => arr. into_iter ( ) . map ( serde_to_val) . collect ( ) ,
66+ serde_json:: Value :: Object ( map) => Val :: obj (
67+ map. into_iter ( )
68+ . map ( |( k, v) | ( Val :: from ( k) , serde_to_val ( v) ) )
69+ . collect ( ) ,
70+ ) ,
71+ }
72+ }
73+
74+ /// Convert jaq Val to serde_json::Value for output formatting.
75+ fn val_to_serde ( v : & Val ) -> serde_json:: Value {
76+ match v {
77+ Val :: Null => serde_json:: Value :: Null ,
78+ Val :: Bool ( b) => serde_json:: Value :: Bool ( * b) ,
79+ Val :: Num ( n) => {
80+ // Use Display to get the number string, then parse
81+ let s = format ! ( "{n}" ) ;
82+ if let Ok ( i) = s. parse :: < i64 > ( ) {
83+ serde_json:: Value :: Number ( serde_json:: Number :: from ( i) )
84+ } else if let Ok ( f) = s. parse :: < f64 > ( ) {
85+ serde_json:: Number :: from_f64 ( f)
86+ . map ( serde_json:: Value :: Number )
87+ . unwrap_or ( serde_json:: Value :: Null )
88+ } else {
89+ serde_json:: Value :: Null
90+ }
91+ }
92+ Val :: BStr ( _) | Val :: TStr ( _) => {
93+ // Extract string bytes and convert to UTF-8
94+ let displayed = format ! ( "{v}" ) ;
95+ // Val's Display wraps strings in quotes — strip them
96+ if displayed. starts_with ( '"' ) && displayed. ends_with ( '"' ) {
97+ // Parse the JSON string to unescape
98+ serde_json:: from_str ( & displayed) . unwrap_or ( serde_json:: Value :: String ( displayed) )
99+ } else {
100+ serde_json:: Value :: String ( displayed)
101+ }
102+ }
103+ Val :: Arr ( a) => serde_json:: Value :: Array ( a. iter ( ) . map ( val_to_serde) . collect ( ) ) ,
104+ Val :: Obj ( o) => {
105+ let map: serde_json:: Map < String , serde_json:: Value > = o
106+ . iter ( )
107+ . map ( |( k, v) | {
108+ let key = match k {
109+ Val :: TStr ( _) | Val :: BStr ( _) => {
110+ let s = format ! ( "{k}" ) ;
111+ if s. starts_with ( '"' ) && s. ends_with ( '"' ) {
112+ serde_json:: from_str :: < String > ( & s) . unwrap_or ( s)
113+ } else {
114+ s
115+ }
116+ }
117+ _ => format ! ( "{k}" ) ,
118+ } ;
119+ ( key, val_to_serde ( v) )
120+ } )
121+ . collect ( ) ;
122+ serde_json:: Value :: Object ( map)
123+ }
124+ }
125+ }
126+
17127/// THREAT[TM-DOS-027]: Maximum nesting depth for JSON input values.
18128/// Prevents stack overflow when jaq evaluates deeply nested JSON structures
19129/// like `[[[[...]]]]` or `{"a":{"a":{"a":...}}}`.
@@ -307,8 +417,11 @@ impl Builtin for Jq {
307417 }
308418
309419 // Set up the loader with standard library definitions
310- let loader = load:: Loader :: new ( jaq_std:: defs ( ) . chain ( jaq_json:: defs ( ) ) ) ;
311- let arena = load:: Arena :: default ( ) ;
420+ let defs = jaq_core:: defs ( )
421+ . chain ( jaq_std:: defs ( ) )
422+ . chain ( jaq_json:: defs ( ) ) ;
423+ let loader = Loader :: new ( defs) ;
424+ let arena = Arena :: default ( ) ;
312425
313426 // Build shell env as a JSON object for the custom `env` filter.
314427 // SECURITY: This avoids calling std::env::set_var() which is
@@ -333,7 +446,7 @@ impl Builtin for Jq {
333446 let filter = compat_filter. as_str ( ) ;
334447
335448 // Parse the filter
336- let program = load :: File {
449+ let program = File {
337450 code : filter,
338451 path : ( ) ,
339452 } ;
@@ -358,9 +471,16 @@ impl Builtin for Jq {
358471 // a def that reads from our injected global variable.
359472 let mut var_names: Vec < & str > = var_bindings. iter ( ) . map ( |( n, _) | n. as_str ( ) ) . collect ( ) ;
360473 var_names. push ( ENV_VAR_NAME ) ;
361- let native_funs = jaq_std:: funs ( )
362- . filter ( |( name, _, _) | * name != "env" )
363- . chain ( jaq_json:: funs ( ) ) ;
474+ type D = InputData < Val > ;
475+ let input_funs: Vec < jaq_core:: native:: Fun < D > > = jaq_std:: input:: funs :: < D > ( )
476+ . into_vec ( )
477+ . into_iter ( )
478+ . map ( |( name, arity, run) | ( name, arity, jaq_core:: Native :: < D > :: new ( run) ) )
479+ . collect ( ) ;
480+ let native_funs = jaq_core:: funs :: < D > ( )
481+ . chain ( jaq_std:: funs :: < D > ( ) . filter ( |( name, _, _) | * name != "env" ) )
482+ . chain ( input_funs)
483+ . chain ( jaq_json:: funs :: < D > ( ) ) ;
364484 let compiler = Compiler :: default ( )
365485 . with_funs ( native_funs)
366486 . with_global_vars ( var_names. iter ( ) . copied ( ) ) ;
@@ -384,26 +504,26 @@ impl Builtin for Jq {
384504 // Build list of inputs to process
385505 let inputs_to_process: Vec < Val > = if null_input {
386506 // -n flag: use null as input
387- vec ! [ Val :: from ( serde_json :: Value :: Null ) ]
507+ vec ! [ Val :: Null ]
388508 } else if raw_input && slurp {
389509 // -Rs flag: read entire input as single string
390- vec ! [ Val :: from( serde_json :: Value :: String ( input. to_string( ) ) ) ]
510+ vec ! [ Val :: from( input. to_string( ) ) ]
391511 } else if raw_input {
392512 // -R flag: each line becomes a JSON string value
393513 input
394514 . lines ( )
395- . map ( |line| Val :: from ( serde_json :: Value :: String ( line. to_string ( ) ) ) )
515+ . map ( |line| Val :: from ( line. to_string ( ) ) )
396516 . collect ( )
397517 } else if slurp {
398518 // -s flag: read all inputs into a single array
399519 match Self :: parse_json_values ( input) {
400- Ok ( vals) => vec ! [ Val :: from ( serde_json:: Value :: Array ( vals) ) ] ,
520+ Ok ( vals) => vec ! [ serde_to_val ( serde_json:: Value :: Array ( vals) ) ] ,
401521 Err ( e) => return Ok ( ExecResult :: err ( format ! ( "{}\n " , e) , 5 ) ) ,
402522 }
403523 } else {
404524 // Parse all JSON values from input (handles multi-line and NDJSON)
405525 match Self :: parse_json_values ( input) {
406- Ok ( json_vals) => json_vals. into_iter ( ) . map ( Val :: from ) . collect ( ) ,
526+ Ok ( json_vals) => json_vals. into_iter ( ) . map ( serde_to_val ) . collect ( ) ,
407527 Err ( e) => return Ok ( ExecResult :: err ( format ! ( "{}\n " , e) , 5 ) ) ,
408528 }
409529 } ;
@@ -414,10 +534,12 @@ impl Builtin for Jq {
414534
415535 // Shared input iterator: main loop pops one value per filter run,
416536 // and jaq's input/inputs functions consume from the same source.
417- let shared_inputs = RcIter :: new ( inputs_to_process. into_iter ( ) . map ( Ok :: < Val , String > ) ) ;
537+ let iter: Box < dyn Iterator < Item = std:: result:: Result < Val , String > > > =
538+ Box :: new ( inputs_to_process. into_iter ( ) . map ( Ok :: < Val , String > ) ) ;
539+ let shared_inputs = RcIter :: new ( iter) ;
418540
419541 // Pre-convert env object to jaq Val once (reused for each input)
420- let env_val = Val :: from ( env_obj) ;
542+ let env_val = serde_to_val ( env_obj) ;
421543
422544 for jaq_input in & shared_inputs {
423545 let jaq_input: Val = match jaq_input {
@@ -431,16 +553,20 @@ impl Builtin for Jq {
431553 // plus the env object as the last global variable.
432554 let mut var_vals: Vec < Val > = var_bindings
433555 . iter ( )
434- . map ( |( _, v) | Val :: from ( v. clone ( ) ) )
556+ . map ( |( _, v) | serde_to_val ( v. clone ( ) ) )
435557 . collect ( ) ;
436558 var_vals. push ( env_val. clone ( ) ) ;
437- let ctx = Ctx :: new ( var_vals, & shared_inputs) ;
438- for result in filter. run ( ( ctx, jaq_input) ) {
559+ let data = InputDataRef {
560+ lut : & filter. lut ,
561+ inputs : & shared_inputs,
562+ } ;
563+ let ctx = Ctx :: < InputData < Val > > :: new ( data, Vars :: new ( var_vals) ) ;
564+ for result in filter. id . run ( ( ctx, jaq_input) ) {
439565 match result {
440566 Ok ( val) => {
441567 has_output = true ;
442568 // Convert back to serde_json::Value and format
443- let json: serde_json :: Value = val . into ( ) ;
569+ let json = val_to_serde ( & val ) ;
444570
445571 // Track for -e exit status
446572 if !matches ! (
0 commit comments