fiddler_script/builtins/
json.rs1use base64::Engine;
4use indexmap::IndexMap;
5
6use crate::error::RuntimeError;
7use crate::Value;
8
9pub fn builtin_parse_json(args: Vec<Value>) -> Result<Value, RuntimeError> {
17 if args.len() != 1 {
18 return Err(RuntimeError::wrong_argument_count(1, args.len()));
19 }
20
21 let bytes = match &args[0] {
22 Value::Bytes(b) => b.clone(),
23 Value::String(s) => s.as_bytes().to_vec(),
24 _ => {
25 return Err(RuntimeError::invalid_argument(
26 "parse_json() requires bytes or string argument".to_string(),
27 ))
28 }
29 };
30
31 let json_value: serde_json::Value = serde_json::from_slice(&bytes)
32 .map_err(|e| RuntimeError::invalid_argument(format!("Invalid JSON: {}", e)))?;
33
34 Ok(json_to_value(json_value))
35}
36
37pub fn json_to_value(json: serde_json::Value) -> Value {
39 match json {
40 serde_json::Value::Null => Value::Null,
41 serde_json::Value::Bool(b) => Value::Boolean(b),
42 serde_json::Value::Number(n) => {
43 if let Some(i) = n.as_i64() {
44 Value::Integer(i)
45 } else if let Some(f) = n.as_f64() {
46 Value::Integer(f as i64)
48 } else {
49 Value::Null
50 }
51 }
52 serde_json::Value::String(s) => Value::String(s),
53 serde_json::Value::Array(arr) => Value::Array(arr.into_iter().map(json_to_value).collect()),
54 serde_json::Value::Object(obj) => {
55 let dict: IndexMap<String, Value> = obj
56 .into_iter()
57 .map(|(k, v)| (k, json_to_value(v)))
58 .collect();
59 Value::Dictionary(dict)
60 }
61 }
62}
63
64pub fn value_to_json(value: &Value) -> serde_json::Value {
66 match value {
67 Value::Null => serde_json::Value::Null,
68 Value::Boolean(b) => serde_json::Value::Bool(*b),
69 Value::Integer(i) => serde_json::Value::Number(serde_json::Number::from(*i)),
70 Value::Float(f) => {
71 if let Some(num) = serde_json::Number::from_f64(*f) {
72 serde_json::Value::Number(num)
73 } else {
74 serde_json::Value::Null
75 }
76 }
77 Value::String(s) => serde_json::Value::String(s.clone()),
78 Value::Bytes(b) => {
79 serde_json::Value::String(base64::engine::general_purpose::STANDARD.encode(b))
81 }
82 Value::Array(arr) => serde_json::Value::Array(arr.iter().map(value_to_json).collect()),
83 Value::Dictionary(dict) => {
84 let obj: serde_json::Map<String, serde_json::Value> = dict
85 .iter()
86 .map(|(k, v)| (k.clone(), value_to_json(v)))
87 .collect();
88 serde_json::Value::Object(obj)
89 }
90 }
91}
92
93pub fn builtin_jmespath(args: Vec<Value>) -> Result<Value, RuntimeError> {
108 if args.len() != 2 {
109 return Err(RuntimeError::wrong_argument_count(2, args.len()));
110 }
111
112 let expression = match &args[1] {
114 Value::String(s) => s.as_str(),
115 _ => {
116 return Err(RuntimeError::invalid_argument(
117 "jmespath() requires a string expression as second argument".to_string(),
118 ))
119 }
120 };
121
122 let json_data = value_to_json(&args[0]);
124
125 let expr = jmespath::compile(expression).map_err(|e| {
127 RuntimeError::invalid_argument(format!("Invalid JMESPath expression: {}", e))
128 })?;
129
130 let data = jmespath::Variable::from_serializable(&json_data).map_err(|e| {
132 RuntimeError::invalid_argument(format!(
133 "Failed to convert data to JMESPath variable: {}",
134 e
135 ))
136 })?;
137
138 let result = expr
140 .search(&data)
141 .map_err(|e| RuntimeError::invalid_argument(format!("JMESPath query failed: {}", e)))?;
142
143 let result_json: serde_json::Value = serde_json::to_value(&*result).map_err(|e| {
146 RuntimeError::invalid_argument(format!("Failed to convert JMESPath result: {}", e))
147 })?;
148
149 Ok(json_to_value(result_json))
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_builtin_parse_json_string() {
158 let json = br#""hello""#.to_vec();
159 let result = builtin_parse_json(vec![Value::Bytes(json)]).unwrap();
160 assert_eq!(result, Value::String("hello".to_string()));
161 }
162
163 #[test]
164 fn test_builtin_parse_json_number() {
165 let json = b"42".to_vec();
166 let result = builtin_parse_json(vec![Value::Bytes(json)]).unwrap();
167 assert_eq!(result, Value::Integer(42));
168 }
169
170 #[test]
171 fn test_builtin_parse_json_boolean() {
172 let json = b"true".to_vec();
173 let result = builtin_parse_json(vec![Value::Bytes(json)]).unwrap();
174 assert_eq!(result, Value::Boolean(true));
175 }
176
177 #[test]
178 fn test_builtin_parse_json_null() {
179 let json = b"null".to_vec();
180 let result = builtin_parse_json(vec![Value::Bytes(json)]).unwrap();
181 assert_eq!(result, Value::Null);
182 }
183
184 #[test]
185 fn test_builtin_parse_json_from_string() {
186 let result =
187 builtin_parse_json(vec![Value::String(r#"{"key": "value"}"#.to_string())]).unwrap();
188 assert!(matches!(result, Value::Dictionary(_)));
189 if let Value::Dictionary(dict) = result {
190 assert_eq!(dict.get("key"), Some(&Value::String("value".to_string())));
191 }
192 }
193
194 #[test]
195 fn test_builtin_parse_json_invalid() {
196 let result = builtin_parse_json(vec![Value::Bytes(b"not valid json".to_vec())]);
197 assert!(matches!(result, Err(RuntimeError::InvalidArgument { .. })));
198 }
199
200 #[test]
201 fn test_value_to_json_null() {
202 let value = Value::Null;
203 let json = value_to_json(&value);
204 assert_eq!(json, serde_json::Value::Null);
205 }
206
207 #[test]
208 fn test_value_to_json_boolean() {
209 let value = Value::Boolean(true);
210 let json = value_to_json(&value);
211 assert_eq!(json, serde_json::Value::Bool(true));
212 }
213
214 #[test]
215 fn test_value_to_json_integer() {
216 let value = Value::Integer(42);
217 let json = value_to_json(&value);
218 assert_eq!(json, serde_json::json!(42));
219 }
220
221 #[test]
222 fn test_value_to_json_float() {
223 let value = Value::Float(3.14);
224 let json = value_to_json(&value);
225 assert_eq!(json, serde_json::json!(3.14));
226 }
227
228 #[test]
229 fn test_value_to_json_string() {
230 let value = Value::String("hello".to_string());
231 let json = value_to_json(&value);
232 assert_eq!(json, serde_json::Value::String("hello".to_string()));
233 }
234
235 #[test]
236 fn test_value_to_json_array() {
237 let value = Value::Array(vec![
238 Value::Integer(1),
239 Value::Integer(2),
240 Value::Integer(3),
241 ]);
242 let json = value_to_json(&value);
243 assert_eq!(json, serde_json::json!([1, 2, 3]));
244 }
245
246 #[test]
247 fn test_value_to_json_dictionary() {
248 let mut dict = IndexMap::new();
249 dict.insert("key".to_string(), Value::String("value".to_string()));
250 let value = Value::Dictionary(dict);
251 let json = value_to_json(&value);
252 assert_eq!(json, serde_json::json!({"key": "value"}));
253 }
254
255 #[test]
256 fn test_builtin_jmespath_simple_key() {
257 let mut dict = IndexMap::new();
258 dict.insert("name".to_string(), Value::String("John".to_string()));
259 dict.insert("age".to_string(), Value::Integer(30));
260 let data = Value::Dictionary(dict);
261
262 let result = builtin_jmespath(vec![data, Value::String("name".to_string())]).unwrap();
263 assert_eq!(result, Value::String("John".to_string()));
264 }
265
266 #[test]
267 fn test_builtin_jmespath_nested_key() {
268 let mut inner_dict = IndexMap::new();
269 inner_dict.insert("bar".to_string(), Value::String("baz".to_string()));
270
271 let mut outer_dict = IndexMap::new();
272 outer_dict.insert("foo".to_string(), Value::Dictionary(inner_dict));
273
274 let data = Value::Dictionary(outer_dict);
275
276 let result = builtin_jmespath(vec![data, Value::String("foo.bar".to_string())]).unwrap();
277 assert_eq!(result, Value::String("baz".to_string()));
278 }
279
280 #[test]
281 fn test_builtin_jmespath_array_index() {
282 let data = Value::Array(vec![
283 Value::Integer(1),
284 Value::Integer(2),
285 Value::Integer(3),
286 ]);
287
288 let result = builtin_jmespath(vec![data, Value::String("[1]".to_string())]).unwrap();
289 assert_eq!(result, Value::Integer(2));
290 }
291
292 #[test]
293 fn test_builtin_jmespath_projection() {
294 let mut dict1 = IndexMap::new();
295 dict1.insert("name".to_string(), Value::String("John".to_string()));
296
297 let mut dict2 = IndexMap::new();
298 dict2.insert("name".to_string(), Value::String("Jane".to_string()));
299
300 let data = Value::Array(vec![Value::Dictionary(dict1), Value::Dictionary(dict2)]);
301
302 let result = builtin_jmespath(vec![data, Value::String("[*].name".to_string())]).unwrap();
303 if let Value::Array(arr) = result {
304 assert_eq!(arr.len(), 2);
305 assert_eq!(arr[0], Value::String("John".to_string()));
306 assert_eq!(arr[1], Value::String("Jane".to_string()));
307 } else {
308 panic!("Expected array result");
309 }
310 }
311
312 #[test]
313 fn test_builtin_jmespath_embedded_key() {
314 let mut inner = IndexMap::new();
315 inner.insert("Key".to_string(), Value::String("value".to_string()));
316
317 let mut outer = IndexMap::new();
318 outer.insert("embedded".to_string(), Value::Dictionary(inner));
319
320 let data = Value::Dictionary(outer);
321
322 let result =
323 builtin_jmespath(vec![data, Value::String("embedded.Key".to_string())]).unwrap();
324 assert_eq!(result, Value::String("value".to_string()));
325 }
326
327 #[test]
328 fn test_builtin_jmespath_nonexistent_key() {
329 let mut dict = IndexMap::new();
330 dict.insert("key".to_string(), Value::String("value".to_string()));
331 let data = Value::Dictionary(dict);
332
333 let result =
334 builtin_jmespath(vec![data, Value::String("nonexistent".to_string())]).unwrap();
335 assert_eq!(result, Value::Null);
336 }
337
338 #[test]
339 fn test_builtin_jmespath_wrong_arg_count() {
340 let result = builtin_jmespath(vec![Value::Null]);
341 assert!(matches!(
342 result,
343 Err(RuntimeError::WrongArgumentCount { .. })
344 ));
345 }
346
347 #[test]
348 fn test_builtin_jmespath_invalid_expression() {
349 let data = Value::Dictionary(IndexMap::new());
350 let result = builtin_jmespath(vec![data, Value::String("[[invalid".to_string())]);
351 assert!(matches!(result, Err(RuntimeError::InvalidArgument { .. })));
352 }
353
354 #[test]
355 fn test_builtin_jmespath_non_string_expression() {
356 let data = Value::Dictionary(IndexMap::new());
357 let result = builtin_jmespath(vec![data, Value::Integer(42)]);
358 assert!(matches!(result, Err(RuntimeError::InvalidArgument { .. })));
359 }
360}