33//! Wraps jaq-core/jaq-std/jaq-json to provide a standard jq CLI interface.
44
55use std:: ffi:: OsString ;
6+ use std:: fs:: File as FsFile ;
67use std:: io:: { self , Read , Write } ;
78
89use jaq_core:: load:: { Arena , File , Loader } ;
@@ -40,6 +41,7 @@ struct JqOptions {
4041 join_output : bool ,
4142 args : Vec < ( String , String ) > ,
4243 jsonargs : Vec < ( String , Val ) > ,
44+ input_paths : Vec < String > ,
4345}
4446
4547fn parse_args ( args : & [ String ] ) -> Result < JqOptions , String > {
@@ -54,6 +56,7 @@ fn parse_args(args: &[String]) -> Result<JqOptions, String> {
5456 join_output : false ,
5557 args : Vec :: new ( ) ,
5658 jsonargs : Vec :: new ( ) ,
59+ input_paths : Vec :: new ( ) ,
5760 } ;
5861
5962 let mut filter_set = false ;
@@ -63,6 +66,17 @@ fn parse_args(args: &[String]) -> Result<JqOptions, String> {
6366 let arg = & args[ i] ;
6467
6568 if arg == "--" {
69+ let remaining = & args[ i + 1 ..] ;
70+ if !filter_set {
71+ let Some ( filter) = remaining. first ( ) else {
72+ return Err ( "no filter provided" . to_string ( ) ) ;
73+ } ;
74+ opts. filter = filter. clone ( ) ;
75+ filter_set = true ;
76+ opts. input_paths . extend ( remaining. iter ( ) . skip ( 1 ) . cloned ( ) ) ;
77+ } else {
78+ opts. input_paths . extend ( remaining. iter ( ) . cloned ( ) ) ;
79+ }
6680 break ;
6781 }
6882
@@ -118,7 +132,7 @@ fn parse_args(args: &[String]) -> Result<JqOptions, String> {
118132 opts. filter = arg. clone ( ) ;
119133 filter_set = true ;
120134 } else {
121- return Err ( format ! ( "unexpected argument: {}" , arg) ) ;
135+ opts . input_paths . push ( arg. clone ( ) ) ;
122136 }
123137
124138 i += 1 ;
@@ -131,30 +145,60 @@ fn parse_args(args: &[String]) -> Result<JqOptions, String> {
131145 Ok ( opts)
132146}
133147
148+ fn read_sources ( opts : & JqOptions ) -> Result < Vec < String > , String > {
149+ if opts. input_paths . is_empty ( ) {
150+ let mut stdin_data = String :: new ( ) ;
151+ io:: stdin ( )
152+ . take ( ( MAX_INPUT_BYTES + 1 ) as u64 )
153+ . read_to_string ( & mut stdin_data)
154+ . map_err ( |e| format ! ( "failed to read stdin: {}" , e) ) ?;
155+ if stdin_data. len ( ) > MAX_INPUT_BYTES {
156+ return Err ( "stdin exceeds size limit" . to_string ( ) ) ;
157+ }
158+ return Ok ( vec ! [ stdin_data] ) ;
159+ }
160+
161+ let mut total = 0usize ;
162+ let mut sources = Vec :: new ( ) ;
163+ for path in & opts. input_paths {
164+ let mut data = String :: new ( ) ;
165+ if path == "-" {
166+ io:: stdin ( )
167+ . take ( ( MAX_INPUT_BYTES . saturating_sub ( total) + 1 ) as u64 )
168+ . read_to_string ( & mut data)
169+ . map_err ( |e| format ! ( "failed to read stdin: {}" , e) ) ?;
170+ } else {
171+ FsFile :: open ( path)
172+ . map_err ( |e| format ! ( "failed to open {}: {}" , path, e) ) ?
173+ . take ( ( MAX_INPUT_BYTES . saturating_sub ( total) + 1 ) as u64 )
174+ . read_to_string ( & mut data)
175+ . map_err ( |e| format ! ( "failed to read {}: {}" , path, e) ) ?;
176+ }
177+ total = total
178+ . checked_add ( data. len ( ) )
179+ . ok_or_else ( || "input exceeds size limit" . to_string ( ) ) ?;
180+ if total > MAX_INPUT_BYTES {
181+ return Err ( "input exceeds size limit" . to_string ( ) ) ;
182+ }
183+ sources. push ( data) ;
184+ }
185+ Ok ( sources)
186+ }
187+
134188fn read_inputs ( opts : & JqOptions ) -> Result < Vec < Val > , String > {
135189 if opts. null_input {
136190 return Ok ( vec ! [ Val :: from( serde_json:: Value :: Null ) ] ) ;
137191 }
138192
139- let mut stdin_data = String :: new ( ) ;
140- io:: stdin ( )
141- . take ( ( MAX_INPUT_BYTES + 1 ) as u64 )
142- . read_to_string ( & mut stdin_data)
143- . map_err ( |e| format ! ( "failed to read stdin: {}" , e) ) ?;
144- if stdin_data. len ( ) > MAX_INPUT_BYTES {
145- return Err ( "stdin exceeds size limit" . to_string ( ) ) ;
146- }
193+ let sources = read_sources ( opts) ?;
147194
148195 if opts. raw_input {
196+ let raw_data = sources. concat ( ) ;
149197 if opts. slurp {
150- let mut arr = Vec :: new ( ) ;
151- for line in stdin_data. lines ( ) {
152- push_input_value ( & mut arr, serde_json:: Value :: String ( line. to_string ( ) ) ) ?;
153- }
154- Ok ( vec ! [ Val :: from( serde_json:: Value :: Array ( arr) ) ] )
198+ Ok ( vec ! [ Val :: from( serde_json:: Value :: String ( raw_data) ) ] )
155199 } else {
156200 let mut lines = Vec :: new ( ) ;
157- for line in stdin_data . lines ( ) {
201+ for line in raw_data . lines ( ) {
158202 push_input_value (
159203 & mut lines,
160204 Val :: from ( serde_json:: Value :: String ( line. to_string ( ) ) ) ,
@@ -163,16 +207,23 @@ fn read_inputs(opts: &JqOptions) -> Result<Vec<Val>, String> {
163207 Ok ( lines)
164208 }
165209 } else {
166- let trimmed = stdin_data. trim ( ) ;
167- if trimmed. is_empty ( ) {
168- return Ok ( vec ! [ Val :: from( serde_json:: Value :: Null ) ] ) ;
210+ let mut values = Vec :: new ( ) ;
211+ for source in & sources {
212+ let trimmed = source. trim ( ) ;
213+ if trimmed. is_empty ( ) {
214+ continue ;
215+ }
216+
217+ let decoder =
218+ serde_json:: Deserializer :: from_str ( trimmed) . into_iter :: < serde_json:: Value > ( ) ;
219+ for result in decoder {
220+ let value = result. map_err ( |e| format ! ( "parse error: {}" , e) ) ?;
221+ push_input_value ( & mut values, value) ?;
222+ }
169223 }
170224
171- let mut values = Vec :: new ( ) ;
172- let decoder = serde_json:: Deserializer :: from_str ( trimmed) . into_iter :: < serde_json:: Value > ( ) ;
173- for result in decoder {
174- let value = result. map_err ( |e| format ! ( "invalid JSON input: {}" , e) ) ?;
175- push_input_value ( & mut values, value) ?;
225+ if values. is_empty ( ) && opts. input_paths . is_empty ( ) {
226+ values. push ( serde_json:: Value :: Null ) ;
176227 }
177228
178229 if opts. slurp {
@@ -217,6 +268,11 @@ fn format_output(val: &Val, opts: &JqOptions) -> Result<String, String> {
217268}
218269
219270fn run_jq ( args : & [ String ] ) -> Result < i32 , String > {
271+ if matches ! ( args, [ arg] if arg == "--version" || arg == "-V" ) {
272+ println ! ( "jq-jaq-{}" , env!( "CARGO_PKG_VERSION" ) ) ;
273+ return Ok ( 0 ) ;
274+ }
275+
220276 let opts = parse_args ( args) ?;
221277 let inputs = read_inputs ( & opts) ?;
222278
0 commit comments