1use super::Error;
7use jq_sys::{
9 jq_compile, jq_get_exit_code, jq_halted, jq_init, jq_next, jq_start, jq_state, jq_teardown, jv,
10 jv_copy, jv_dump_string, jv_free, jv_get_kind, jv_invalid_get_msg, jv_invalid_has_msg,
11 jv_kind_JV_KIND_INVALID, jv_kind_JV_KIND_NUMBER, jv_number_value, jv_parser, jv_parser_free,
12 jv_parser_new, jv_parser_next, jv_parser_set_buf, jv_string_value,
13};
14use std::ffi::{CStr, CString};
15use std::os::raw::c_char;
16
17pub struct Jq {
18 state: *mut jq_state,
19}
20
21impl Jq {
22 pub fn compile_program(program: CString) -> Result<Self, Error> {
23 let jq = Jq {
24 state: unsafe {
25 let ptr = jq_init();
28 if ptr.is_null() {
29 return Err(Error::System {
30 msg: Some("Failed to init".into()),
31 });
32 } else {
33 ptr
34 }
35 },
36 };
37 unsafe {
38 if jq_compile(jq.state, program.as_ptr()) == 0 {
39 Err(Error::Compile)
40 } else {
41 Ok(jq)
42 }
43 }
44 }
45
46 fn is_halted(&self) -> bool {
47 unsafe { jq_halted(self.state) != 0 }
48 }
49
50 fn get_exit_code(&self) -> ExitCode {
51 let exit_code = JV {
52 ptr: unsafe { jq_get_exit_code(self.state) },
53 };
54
55 if exit_code.is_valid() {
59 ExitCode::JQ_OK
60 } else {
61 exit_code
62 .as_number()
63 .map(|i| (i as isize).into())
64 .unwrap_or(ExitCode::JQ_ERROR_UNKNOWN)
65 }
66 }
67
68 pub fn execute(&mut self, input: CString) -> Result<String, Error> {
70 let mut parser = Parser::new();
71 self.process(parser.parse(input)?)
72 }
73
74 fn process(&mut self, initial_value: JV) -> Result<String, Error> {
79 let mut buf = String::new();
80
81 unsafe {
82 jq_start(self.state, initial_value.ptr, 0);
83
84 dump(self, &mut buf)?;
85 }
86 let len = buf.trim_end().len();
88 buf.truncate(len);
89
90 Ok(buf)
91 }
92}
93
94impl Drop for Jq {
95 fn drop(&mut self) {
96 unsafe { jq_teardown(&mut self.state) }
97 }
98}
99
100struct JV {
101 ptr: jv,
102}
103
104impl JV {
105 pub fn as_dump_string(&self) -> Result<String, std::str::Utf8Error> {
107 let dump = JV {
108 ptr: unsafe { jv_dump_string(self.ptr, 0) },
109 };
110 unsafe { get_string_value(jv_string_value(dump.ptr)) }
111 }
112
113 pub fn get_msg(&self) -> Option<String> {
115 if self.invalid_has_msg() {
116 let reason = {
117 let msg = JV {
118 ptr: unsafe {
119 jv_invalid_get_msg(jv_copy(self.ptr))
124 },
125 };
126
127 let s = unsafe { get_string_value(jv_string_value(msg.ptr)) };
128
129 format!("Parse error: {}", s.unwrap_or_else(|_| "unknown".into()))
130 };
131 Some(reason)
132 } else {
133 None
134 }
135 }
136
137 pub fn as_number(&self) -> Option<f64> {
138 unsafe {
139 if jv_get_kind(self.ptr) == jv_kind_JV_KIND_NUMBER {
140 Some(jv_number_value(self.ptr))
141 } else {
142 None
143 }
144 }
145 }
146
147 pub fn is_valid(&self) -> bool {
148 unsafe {
149 jv_get_kind(jv_copy(self.ptr)) != jv_kind_JV_KIND_INVALID
155 }
156 }
157
158 pub fn invalid_has_msg(&self) -> bool {
159 unsafe { jv_invalid_has_msg(self.ptr) == 1 }
163 }
164}
165
166impl Drop for JV {
167 fn drop(&mut self) {
168 unsafe { jv_free(self.ptr) };
169 }
170}
171
172struct Parser {
173 ptr: *mut jv_parser,
174}
175
176impl Parser {
177 pub fn new() -> Self {
178 Self {
179 ptr: unsafe { jv_parser_new(0) },
180 }
181 }
182
183 pub fn parse(&mut self, input: CString) -> Result<JV, Error> {
184 let is_last = 0;
189
190 unsafe {
196 jv_parser_set_buf(
197 self.ptr,
198 input.as_ptr(),
199 input.as_bytes().len() as i32,
200 is_last,
201 )
202 };
203
204 let value = JV {
205 ptr: unsafe { jv_parser_next(self.ptr) },
206 };
207 if value.is_valid() {
208 Ok(value)
209 } else {
210 Err(Error::System {
211 msg: Some(
212 value
213 .get_msg()
214 .unwrap_or_else(|| "Parser error".to_string()),
215 ),
216 })
217 }
218 }
219}
220
221impl Drop for Parser {
222 fn drop(&mut self) {
223 unsafe {
224 jv_parser_free(self.ptr);
225 }
226 }
227}
228
229unsafe fn get_string_value(value: *const c_char) -> Result<String, std::str::Utf8Error> {
231 let s = CStr::from_ptr(value).to_str()?;
232 Ok(s.to_owned())
233}
234
235unsafe fn dump(jq: &Jq, buf: &mut String) -> Result<(), Error> {
237 let mut value = JV {
240 ptr: jq_next(jq.state),
241 };
242
243 while value.is_valid() {
244 match value.as_dump_string() {
245 Ok(s) => {
246 buf.push_str(&s);
247 buf.push('\n');
248 }
249 Err(e) => {
250 return Err(Error::System {
251 msg: Some(format!("String Decode error: {}", e)),
252 });
253 }
254 };
255
256 value = JV {
257 ptr: jq_next(jq.state),
258 };
259 }
260
261 if jq.is_halted() {
262 use self::ExitCode::*;
263 match jq.get_exit_code() {
264 JQ_ERROR_SYSTEM => Err(Error::System {
265 msg: value.get_msg(),
266 }),
267 JQ_ERROR_COMPILE => Err(Error::Compile),
273 JQ_OK | JQ_OK_NULL_KIND | JQ_OK_NO_OUTPUT => Ok(()),
277 JQ_ERROR_UNKNOWN => Err(Error::Unknown),
278 }
279 } else if let Some(reason) = value.get_msg() {
280 Err(Error::System { msg: Some(reason) })
281 } else {
282 Ok(())
283 }
284}
285
286#[allow(non_camel_case_types, dead_code)]
294enum ExitCode {
295 JQ_OK = 0,
296 JQ_OK_NULL_KIND = -1,
297 JQ_ERROR_SYSTEM = 2,
298 JQ_ERROR_COMPILE = 3,
299 JQ_OK_NO_OUTPUT = -4,
300 JQ_ERROR_UNKNOWN = 5,
301}
302
303impl From<isize> for ExitCode {
304 fn from(number: isize) -> Self {
305 use self::ExitCode::*;
306 match number as isize {
307 n if n == JQ_OK as isize => JQ_OK,
308 n if n == JQ_OK_NULL_KIND as isize => JQ_OK_NULL_KIND,
309 n if n == JQ_ERROR_SYSTEM as isize => JQ_ERROR_SYSTEM,
310 n if n == JQ_ERROR_COMPILE as isize => JQ_ERROR_COMPILE,
311 n if n == JQ_OK_NO_OUTPUT as isize => JQ_OK_NO_OUTPUT,
312 n if n == JQ_ERROR_UNKNOWN as isize => JQ_ERROR_UNKNOWN,
313 _ => JQ_ERROR_UNKNOWN,
316 }
317 }
318}