1use crate::ast::{BinOp, Expr, Step};
15use crate::comment::Commented;
16use crate::strings;
17use crate::value::Value;
18use std::cmp::Ordering;
19
20#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
22#[error("{msg}")]
23pub struct EvalError {
24 pub msg: String,
25}
26
27impl EvalError {
28 pub(crate) fn new(msg: impl Into<String>) -> EvalError {
29 EvalError { msg: msg.into() }
30 }
31}
32
33pub fn eval_with_comments(expr: &Expr, root: &Commented) -> Result<Vec<Value>, EvalError> {
41 if !expr.has_comment() {
42 return eval(expr, &root.to_value());
43 }
44 match expr {
45 Expr::Path(steps) => Ok(root.resolve_comment(steps)),
46 Expr::Call(name, args) if name == "comments" && args.is_empty() => {
48 Ok(comment_records(root))
49 }
50 Expr::Pipe(a, b) => {
51 let mut out = Vec::new();
52 for v in eval_with_comments(a, root)? {
53 out.extend(eval(b, &v)?);
55 }
56 Ok(out)
57 }
58 Expr::Alternative(a, b) => {
59 let truthy: Vec<Value> = eval_with_comments(a, root)?
60 .into_iter()
61 .filter(Value::is_truthy)
62 .collect();
63 if truthy.is_empty() {
64 eval_with_comments(b, root)
65 } else {
66 Ok(truthy)
67 }
68 }
69 Expr::Comma(items) => {
70 let mut out = Vec::new();
71 for it in items {
72 out.extend(eval_with_comments(it, root)?);
73 }
74 Ok(out)
75 }
76 Expr::Collect(inner) => {
77 let items = match inner {
78 Some(e) => eval_with_comments(e, root)?,
79 None => Vec::new(),
80 };
81 Ok(vec![Value::Array(items)])
82 }
83 _ => Err(EvalError::new(
84 "comment access (`#` / `comments`) here isn't supported: use a comment \
85 path (`.foo.#`) or the `comments` stream, optionally piped or collected",
86 )),
87 }
88}
89
90fn comment_records(root: &Commented) -> Vec<Value> {
95 root.comment_targets()
96 .into_iter()
97 .map(|(steps, kind, text)| {
98 Value::Object(vec![
99 ("path".into(), Value::Str(crate::render_path(&steps))),
100 ("kind".into(), Value::Str(kind.as_str().to_string())),
101 ("text".into(), Value::Str(text)),
102 ])
103 })
104 .collect()
105}
106
107pub fn eval(expr: &Expr, input: &Value) -> Result<Vec<Value>, EvalError> {
109 match expr {
110 Expr::Path(steps) => eval_path(steps, input),
111 Expr::Literal(v) => Ok(vec![v.clone()]),
112 Expr::Neg(inner) => {
113 let mut out = Vec::new();
114 for v in eval(inner, input)? {
115 out.push(negate(&v)?);
116 }
117 Ok(out)
118 }
119 Expr::Binary(op, l, r) => {
120 let lefts = eval(l, input)?;
121 let rights = eval(r, input)?;
122 let mut out = Vec::new();
123 for a in &lefts {
124 for b in &rights {
125 out.push(binary(*op, a, b)?);
126 }
127 }
128 Ok(out)
129 }
130 Expr::Pipe(l, r) => {
131 let mut out = Vec::new();
132 for v in eval(l, input)? {
133 out.extend(eval(r, &v)?);
134 }
135 Ok(out)
136 }
137 Expr::Alternative(l, r) => {
138 let truthy: Vec<Value> = eval(l, input)?
143 .into_iter()
144 .filter(Value::is_truthy)
145 .collect();
146 if truthy.is_empty() {
147 eval(r, input)
148 } else {
149 Ok(truthy)
150 }
151 }
152 Expr::Comma(items) => {
153 let mut out = Vec::new();
154 for it in items {
155 out.extend(eval(it, input)?);
156 }
157 Ok(out)
158 }
159 Expr::Call(name, args) => eval_call(name, args, input),
160 Expr::Collect(inner) => {
161 let items = match inner {
162 Some(e) => eval(e, input)?,
163 None => Vec::new(),
164 };
165 Ok(vec![Value::Array(items)])
166 }
167 Expr::ObjectConstruct(pairs) => {
168 let mut obj = Vec::with_capacity(pairs.len());
169 for (key, value_expr) in pairs {
170 let v = eval(value_expr, input)?
171 .into_iter()
172 .next()
173 .unwrap_or(Value::Null);
174 obj.push((key.clone(), v));
175 }
176 Ok(vec![Value::Object(obj)])
177 }
178 Expr::Assign(lhs, rhs) => {
179 let steps = assign_path(lhs)?;
180 let mut out = Vec::new();
181 for rv in eval(rhs, input)? {
182 out.push(set_path(input, steps, &rv)?);
183 }
184 Ok(out)
185 }
186 Expr::UpdateAssign(lhs, rhs) => {
187 let steps = assign_path(lhs)?;
188 Ok(vec![update_path(input, steps, rhs)?])
189 }
190 Expr::AddAssign(lhs, rhs) => {
191 let steps = assign_path(lhs)?;
192 let mut out = Vec::new();
193 for rv in eval(rhs, input)? {
194 let current = eval_path(steps, input)?
195 .into_iter()
196 .next()
197 .unwrap_or(Value::Null);
198 let sum = binary(BinOp::Add, ¤t, &rv)?;
199 out.push(set_path(input, steps, &sum)?);
200 }
201 Ok(out)
202 }
203 }
204}
205
206fn assign_path(expr: &Expr) -> Result<&[Step], EvalError> {
208 expr.as_path()
209 .ok_or_else(|| EvalError::new("left side of an assignment must be a path"))
210}
211
212fn set_path(v: &Value, steps: &[Step], new: &Value) -> Result<Value, EvalError> {
215 let Some((head, rest)) = steps.split_first() else {
216 return Ok(new.clone());
217 };
218 match head {
219 Step::Field(k) => {
220 let mut obj = match v {
221 Value::Object(m) => m.clone(),
222 Value::Null => Vec::new(),
223 other => {
224 return Err(EvalError::new(format!(
225 "cannot set field of {}",
226 other.type_name()
227 )));
228 }
229 };
230 match obj.iter_mut().find(|(kk, _)| kk == k) {
231 Some(pair) => pair.1 = set_path(&pair.1, rest, new)?,
232 None => obj.push((k.clone(), set_path(&Value::Null, rest, new)?)),
233 }
234 Ok(Value::Object(obj))
235 }
236 Step::Index(i) => {
237 let mut arr = match v {
238 Value::Array(a) => a.clone(),
239 Value::Null => Vec::new(),
240 other => {
241 return Err(EvalError::new(format!(
242 "cannot index {} with a number",
243 other.type_name()
244 )));
245 }
246 };
247 let idx = if *i < 0 { arr.len() as i64 + i } else { *i };
248 if idx < 0 {
249 return Err(EvalError::new("array index out of range"));
250 }
251 let idx = idx as usize;
252 if idx >= arr.len() {
253 arr.resize(idx + 1, Value::Null);
254 }
255 arr[idx] = set_path(&arr[idx], rest, new)?;
256 Ok(Value::Array(arr))
257 }
258 Step::Iterate => match v {
259 Value::Array(a) => {
260 let mut out = Vec::with_capacity(a.len());
261 for e in a {
262 out.push(set_path(e, rest, new)?);
263 }
264 Ok(Value::Array(out))
265 }
266 Value::Object(m) => {
267 let mut out = Vec::with_capacity(m.len());
268 for (k, e) in m {
269 out.push((k.clone(), set_path(e, rest, new)?));
270 }
271 Ok(Value::Object(out))
272 }
273 other => Err(EvalError::new(format!(
274 "cannot iterate over {}",
275 other.type_name()
276 ))),
277 },
278 Step::Comment(_) => Err(EvalError::new(comment_mutation_unsupported())),
279 }
280}
281
282fn update_path(v: &Value, steps: &[Step], f: &Expr) -> Result<Value, EvalError> {
284 let Some((head, rest)) = steps.split_first() else {
285 return Ok(eval(f, v)?.into_iter().next().unwrap_or(Value::Null));
286 };
287 match head {
288 Step::Field(k) => {
289 let mut obj = match v {
290 Value::Object(m) => m.clone(),
291 other => {
292 return Err(EvalError::new(format!(
293 "cannot update field of {}",
294 other.type_name()
295 )));
296 }
297 };
298 match obj.iter_mut().find(|(kk, _)| kk == k) {
299 Some(pair) => pair.1 = update_path(&pair.1, rest, f)?,
300 None => return Err(EvalError::new(format!("no such key: \"{k}\""))),
301 }
302 Ok(Value::Object(obj))
303 }
304 Step::Index(i) => {
305 let mut arr = match v {
306 Value::Array(a) => a.clone(),
307 other => {
308 return Err(EvalError::new(format!(
309 "cannot index {} with a number",
310 other.type_name()
311 )));
312 }
313 };
314 let idx = if *i < 0 { arr.len() as i64 + i } else { *i };
315 if idx < 0 || idx as usize >= arr.len() {
316 return Err(EvalError::new("array index out of range"));
317 }
318 let idx = idx as usize;
319 arr[idx] = update_path(&arr[idx], rest, f)?;
320 Ok(Value::Array(arr))
321 }
322 Step::Iterate => match v {
323 Value::Array(a) => {
324 let mut out = Vec::with_capacity(a.len());
325 for e in a {
326 out.push(update_path(e, rest, f)?);
327 }
328 Ok(Value::Array(out))
329 }
330 Value::Object(m) => {
331 let mut out = Vec::with_capacity(m.len());
332 for (k, e) in m {
333 out.push((k.clone(), update_path(e, rest, f)?));
334 }
335 Ok(Value::Object(out))
336 }
337 other => Err(EvalError::new(format!(
338 "cannot iterate over {}",
339 other.type_name()
340 ))),
341 },
342 Step::Comment(_) => Err(EvalError::new(comment_mutation_unsupported())),
343 }
344}
345
346fn eval_path(steps: &[Step], input: &Value) -> Result<Vec<Value>, EvalError> {
347 let mut stream = vec![input.clone()];
348 for step in steps {
349 let mut next = Vec::new();
350 for v in &stream {
351 next.extend(apply_step(step, v)?);
352 }
353 stream = next;
354 }
355 Ok(stream)
356}
357
358fn apply_step(step: &Step, v: &Value) -> Result<Vec<Value>, EvalError> {
359 match step {
360 Step::Field(k) => match v {
361 Value::Object(m) => Ok(m
362 .iter()
363 .find(|(kk, _)| kk == k)
364 .map(|(_, val)| vec![val.clone()])
365 .unwrap_or_default()),
366 Value::Null => Ok(vec![]),
367 other => Err(EvalError::new(format!(
368 "cannot index {} with \"{k}\"",
369 other.type_name()
370 ))),
371 },
372 Step::Index(i) => match v {
373 Value::Array(a) => {
374 let idx = if *i < 0 { a.len() as i64 + i } else { *i };
375 if idx >= 0 && (idx as usize) < a.len() {
376 Ok(vec![a[idx as usize].clone()])
377 } else {
378 Ok(vec![])
379 }
380 }
381 Value::Null => Ok(vec![]),
382 other => Err(EvalError::new(format!(
383 "cannot index {} with a number",
384 other.type_name()
385 ))),
386 },
387 Step::Iterate => match v {
388 Value::Array(a) => Ok(a.clone()),
389 Value::Object(m) => Ok(m.iter().map(|(_, val)| val.clone()).collect()),
390 other => Err(EvalError::new(format!(
391 "cannot iterate over {}",
392 other.type_name()
393 ))),
394 },
395 Step::Comment(_) => Err(EvalError::new(
399 "comment access (`#`) resolves only as a whole path like `.foo.#`, \
400 not after a pipe over a value",
401 )),
402 }
403}
404
405fn negate(v: &Value) -> Result<Value, EvalError> {
406 match v {
407 Value::Int(i) => Ok(Value::Int(-i)),
408 Value::Float(f) => Ok(Value::Float(-f)),
409 other => Err(EvalError::new(format!(
410 "cannot negate {}",
411 other.type_name()
412 ))),
413 }
414}
415
416fn binary(op: BinOp, a: &Value, b: &Value) -> Result<Value, EvalError> {
417 match op {
418 BinOp::Eq => Ok(Value::Bool(a.value_eq(b))),
419 BinOp::Ne => Ok(Value::Bool(!a.value_eq(b))),
420 BinOp::Lt => Ok(Value::Bool(a.order(b) == Ordering::Less)),
421 BinOp::Gt => Ok(Value::Bool(a.order(b) == Ordering::Greater)),
422 BinOp::Le => Ok(Value::Bool(a.order(b) != Ordering::Greater)),
423 BinOp::Ge => Ok(Value::Bool(a.order(b) != Ordering::Less)),
424 BinOp::Add => add(a, b),
425 BinOp::Sub => arith(a, b, |x, y| x - y, i64::checked_sub, "subtract"),
426 BinOp::Mul => arith(a, b, |x, y| x * y, i64::checked_mul, "multiply"),
427 BinOp::Div => divide(a, b),
428 BinOp::Mod => modulo(a, b),
429 }
430}
431
432fn add(a: &Value, b: &Value) -> Result<Value, EvalError> {
435 match (a, b) {
436 (Value::Null, _) => Ok(b.clone()),
437 (_, Value::Null) => Ok(a.clone()),
438 (Value::Str(x), Value::Str(y)) => Ok(Value::Str(format!("{x}{y}"))),
439 (Value::Array(x), Value::Array(y)) => {
440 let mut v = x.clone();
441 v.extend(y.clone());
442 Ok(Value::Array(v))
443 }
444 _ => arith(a, b, |x, y| x + y, i64::checked_add, "add"),
445 }
446}
447
448fn arith(
449 a: &Value,
450 b: &Value,
451 f: impl Fn(f64, f64) -> f64,
452 checked: impl Fn(i64, i64) -> Option<i64>,
453 verb: &str,
454) -> Result<Value, EvalError> {
455 match (a, b) {
456 (Value::Int(x), Value::Int(y)) => match checked(*x, *y) {
457 Some(r) => Ok(Value::Int(r)),
458 None => Ok(Value::Float(f(*x as f64, *y as f64))),
459 },
460 _ => match (a.as_f64(), b.as_f64()) {
461 (Some(x), Some(y)) => Ok(Value::Float(f(x, y))),
462 _ => Err(EvalError::new(format!(
463 "cannot {verb} {} and {}",
464 a.type_name(),
465 b.type_name()
466 ))),
467 },
468 }
469}
470
471fn divide(a: &Value, b: &Value) -> Result<Value, EvalError> {
472 match (a.as_f64(), b.as_f64()) {
473 (Some(x), Some(y)) => {
474 if y == 0.0 {
475 return Err(EvalError::new("division by zero"));
476 }
477 match (a, b) {
480 (Value::Int(xi), Value::Int(yi)) if *xi % *yi == 0 => Ok(Value::Int(*xi / *yi)),
481 _ => Ok(Value::Float(x / y)),
482 }
483 }
484 _ => Err(EvalError::new(format!(
485 "cannot divide {} and {}",
486 a.type_name(),
487 b.type_name()
488 ))),
489 }
490}
491
492fn modulo(a: &Value, b: &Value) -> Result<Value, EvalError> {
493 match (a.as_f64(), b.as_f64()) {
494 (Some(x), Some(y)) => {
495 if y == 0.0 {
496 return Err(EvalError::new("division by zero"));
497 }
498 if let (Value::Int(xi), Value::Int(yi)) = (a, b) {
499 return Ok(Value::Int(*xi % *yi));
500 }
501 Ok(Value::Float(x % y))
502 }
503 _ => Err(EvalError::new(format!(
504 "cannot mod {} and {}",
505 a.type_name(),
506 b.type_name()
507 ))),
508 }
509}
510
511fn eval_call(name: &str, args: &[Expr], input: &Value) -> Result<Vec<Value>, EvalError> {
512 let arity = |n: usize| -> Result<(), EvalError> {
513 if args.len() == n {
514 Ok(())
515 } else {
516 Err(EvalError::new(format!(
517 "{name} takes {n} argument(s), got {}",
518 args.len()
519 )))
520 }
521 };
522 let arity_between = |min: usize, max: usize| -> Result<(), EvalError> {
524 if (min..=max).contains(&args.len()) {
525 Ok(())
526 } else {
527 Err(EvalError::new(format!(
528 "{name} takes {min}-{max} arguments, got {}",
529 args.len()
530 )))
531 }
532 };
533 let flags_arg = |at: usize| -> Result<String, EvalError> {
535 match args.get(at) {
536 Some(a) => str_arg(a, input, "flags"),
537 None => Ok(String::new()),
538 }
539 };
540
541 match name {
542 "select" => {
543 arity(1)?;
544 let mut out = Vec::new();
545 for cond in eval(&args[0], input)? {
546 if cond.is_truthy() {
547 out.push(input.clone());
548 }
549 }
550 Ok(out)
551 }
552 "length" => {
553 arity(0)?;
554 Ok(vec![length(input)?])
555 }
556 "keys" => {
557 arity(0)?;
558 Ok(vec![keys(input)?])
559 }
560 "type" => {
561 arity(0)?;
562 Ok(vec![Value::Str(input.type_name().to_string())])
563 }
564 "tostring" => {
565 arity(0)?;
566 Ok(vec![Value::Str(input.to_raw_string())])
567 }
568 "tonumber" => {
569 arity(0)?;
570 Ok(vec![tonumber(input)?])
571 }
572 "ascii_upcase" => {
573 arity(0)?;
574 Ok(vec![map_str(input, |s| s.to_uppercase())?])
575 }
576 "ascii_downcase" => {
577 arity(0)?;
578 Ok(vec![map_str(input, |s| s.to_lowercase())?])
579 }
580 "has" => {
581 arity(1)?;
582 let mut out = Vec::new();
583 for key in eval(&args[0], input)? {
584 out.push(Value::Bool(has(input, &key)?));
585 }
586 Ok(out)
587 }
588 "ltrimstr" => {
589 arity(1)?;
590 trim_str(input, &args[0], true)
591 }
592 "rtrimstr" => {
593 arity(1)?;
594 trim_str(input, &args[0], false)
595 }
596 "startswith" | "endswith" => {
597 arity(1)?;
598 let s = str_input(input, name)?;
599 let affix = str_arg(&args[0], input, "the affix")?;
600 let hit = if name == "startswith" {
601 s.starts_with(&affix)
602 } else {
603 s.ends_with(&affix)
604 };
605 Ok(vec![Value::Bool(hit)])
606 }
607 "test" => {
608 arity_between(1, 2)?;
609 let re = str_arg(&args[0], input, "the regex")?;
610 Ok(vec![strings::test(
611 str_input(input, name)?,
612 &re,
613 &flags_arg(1)?,
614 )?])
615 }
616 "match" => {
617 arity_between(1, 2)?;
618 let re = str_arg(&args[0], input, "the regex")?;
619 strings::find(str_input(input, name)?, &re, &flags_arg(1)?)
620 }
621 "capture" => {
622 arity_between(1, 2)?;
623 let re = str_arg(&args[0], input, "the regex")?;
624 strings::capture(str_input(input, name)?, &re, &flags_arg(1)?)
625 }
626 "sub" | "gsub" => {
627 arity_between(2, 3)?;
628 let re = str_arg(&args[0], input, "the regex")?;
629 let repl = str_arg(&args[1], input, "the replacement")?;
630 let mut flags = flags_arg(2)?;
631 if name == "gsub" {
632 flags.push('g');
633 }
634 Ok(vec![strings::sub(
635 str_input(input, name)?,
636 &re,
637 &repl,
638 &flags,
639 )?])
640 }
641 "split" => {
642 arity_between(1, 2)?;
643 let sep = str_arg(&args[0], input, "the separator")?;
644 let regex_flags = if args.len() == 2 {
646 Some(flags_arg(1)?)
647 } else {
648 None
649 };
650 Ok(vec![strings::split(
651 str_input(input, name)?,
652 &sep,
653 regex_flags.as_deref(),
654 )?])
655 }
656 "join" => {
657 arity(1)?;
658 let Value::Array(items) = input else {
659 return Err(EvalError::new(format!(
660 "join requires an array input, got {}",
661 input.type_name()
662 )));
663 };
664 let sep = str_arg(&args[0], input, "the separator")?;
665 Ok(vec![strings::join(items, &sep)?])
666 }
667 "del" => {
668 arity(1)?;
669 let steps = args[0]
670 .as_path()
671 .ok_or_else(|| EvalError::new("del(...) takes a path"))?;
672 Ok(vec![delete_path(input, steps)?])
673 }
674 _ => Err(EvalError::new(format!("unknown function `{name}`"))),
675 }
676}
677
678fn delete_path(v: &Value, steps: &[Step]) -> Result<Value, EvalError> {
681 let Some((head, rest)) = steps.split_first() else {
682 return Err(EvalError::new("del(.) is not allowed"));
683 };
684 if rest.is_empty() {
685 return remove_step(v, head);
686 }
687 match head {
688 Step::Field(k) => {
689 let mut obj = match v {
690 Value::Object(m) => m.clone(),
691 Value::Null => return Ok(Value::Null),
692 other => {
693 return Err(EvalError::new(format!(
694 "cannot descend into {}",
695 other.type_name()
696 )));
697 }
698 };
699 if let Some(pair) = obj.iter_mut().find(|(kk, _)| kk == k) {
700 pair.1 = delete_path(&pair.1, rest)?;
701 }
702 Ok(Value::Object(obj))
703 }
704 Step::Index(i) => {
705 let mut arr = match v {
706 Value::Array(a) => a.clone(),
707 Value::Null => return Ok(Value::Null),
708 other => {
709 return Err(EvalError::new(format!(
710 "cannot index {} with a number",
711 other.type_name()
712 )));
713 }
714 };
715 let idx = if *i < 0 { arr.len() as i64 + i } else { *i };
716 if idx >= 0 && (idx as usize) < arr.len() {
717 let idx = idx as usize;
718 arr[idx] = delete_path(&arr[idx], rest)?;
719 }
720 Ok(Value::Array(arr))
721 }
722 Step::Iterate => match v {
723 Value::Array(a) => {
724 let mut out = Vec::with_capacity(a.len());
725 for e in a {
726 out.push(delete_path(e, rest)?);
727 }
728 Ok(Value::Array(out))
729 }
730 Value::Object(m) => {
731 let mut out = Vec::with_capacity(m.len());
732 for (k, e) in m {
733 out.push((k.clone(), delete_path(e, rest)?));
734 }
735 Ok(Value::Object(out))
736 }
737 other => Err(EvalError::new(format!(
738 "cannot iterate over {}",
739 other.type_name()
740 ))),
741 },
742 Step::Comment(_) => Err(EvalError::new(comment_mutation_unsupported())),
743 }
744}
745
746fn remove_step(v: &Value, step: &Step) -> Result<Value, EvalError> {
748 match step {
749 Step::Field(k) => match v {
750 Value::Object(m) => {
751 let kept = m.iter().filter(|(kk, _)| kk != k).cloned().collect();
752 Ok(Value::Object(kept))
753 }
754 Value::Null => Ok(Value::Null),
755 other => Err(EvalError::new(format!(
756 "cannot delete a field of {}",
757 other.type_name()
758 ))),
759 },
760 Step::Index(i) => match v {
761 Value::Array(a) => {
762 let mut arr = a.clone();
763 let idx = if *i < 0 { arr.len() as i64 + i } else { *i };
764 if idx >= 0 && (idx as usize) < arr.len() {
765 arr.remove(idx as usize);
766 }
767 Ok(Value::Array(arr))
768 }
769 Value::Null => Ok(Value::Null),
770 other => Err(EvalError::new(format!(
771 "cannot delete an index of {}",
772 other.type_name()
773 ))),
774 },
775 Step::Iterate => match v {
776 Value::Array(_) => Ok(Value::Array(Vec::new())),
777 Value::Object(_) => Ok(Value::Object(Vec::new())),
778 other => Err(EvalError::new(format!(
779 "cannot iterate over {}",
780 other.type_name()
781 ))),
782 },
783 Step::Comment(_) => Err(EvalError::new(comment_mutation_unsupported())),
784 }
785}
786
787fn comment_mutation_unsupported() -> &'static str {
789 "editing comments (`#`) is not supported yet (planned for v0.2); reading works - e.g. `edikt '.foo.#' file`"
790}
791
792fn length(v: &Value) -> Result<Value, EvalError> {
793 let n = match v {
794 Value::Null => 0,
795 Value::Str(s) => s.chars().count() as i64,
796 Value::Array(a) => a.len() as i64,
797 Value::Object(m) => m.len() as i64,
798 other => {
799 return Err(EvalError::new(format!(
800 "{} has no length",
801 other.type_name()
802 )));
803 }
804 };
805 Ok(Value::Int(n))
806}
807
808fn keys(v: &Value) -> Result<Value, EvalError> {
809 match v {
810 Value::Object(m) => {
811 let mut ks: Vec<String> = m.iter().map(|(k, _)| k.clone()).collect();
812 ks.sort(); Ok(Value::Array(ks.into_iter().map(Value::Str).collect()))
814 }
815 Value::Array(a) => Ok(Value::Array((0..a.len() as i64).map(Value::Int).collect())),
816 other => Err(EvalError::new(format!("{} has no keys", other.type_name()))),
817 }
818}
819
820fn tonumber(v: &Value) -> Result<Value, EvalError> {
821 match v {
822 Value::Int(_) | Value::Float(_) => Ok(v.clone()),
823 Value::Str(s) => {
824 let t = s.trim();
825 if let Ok(i) = t.parse::<i64>() {
826 Ok(Value::Int(i))
827 } else if let Ok(f) = t.parse::<f64>() {
828 Ok(Value::Float(f))
829 } else {
830 Err(EvalError::new(format!("cannot parse \"{s}\" as a number")))
831 }
832 }
833 other => Err(EvalError::new(format!(
834 "cannot parse {} as a number",
835 other.type_name()
836 ))),
837 }
838}
839
840fn str_input<'a>(input: &'a Value, name: &str) -> Result<&'a str, EvalError> {
842 match input {
843 Value::Str(s) => Ok(s),
844 other => Err(EvalError::new(format!(
845 "{name} requires a string input, got {}",
846 other.type_name()
847 ))),
848 }
849}
850
851fn str_arg(arg: &Expr, input: &Value, what: &str) -> Result<String, EvalError> {
853 match eval(arg, input)?.into_iter().next() {
854 Some(Value::Str(s)) => Ok(s),
855 Some(other) => Err(EvalError::new(format!(
856 "{what} must be a string, got {}",
857 other.type_name()
858 ))),
859 None => Err(EvalError::new(format!("{what} produced no value"))),
860 }
861}
862
863fn map_str(v: &Value, f: impl Fn(&str) -> String) -> Result<Value, EvalError> {
864 match v {
865 Value::Str(s) => Ok(Value::Str(f(s))),
866 other => Err(EvalError::new(format!(
867 "expected a string, got {}",
868 other.type_name()
869 ))),
870 }
871}
872
873fn has(v: &Value, key: &Value) -> Result<bool, EvalError> {
874 match (v, key) {
875 (Value::Object(m), Value::Str(k)) => Ok(m.iter().any(|(kk, _)| kk == k)),
876 (Value::Array(a), Value::Int(i)) => Ok(*i >= 0 && (*i as usize) < a.len()),
877 _ => Err(EvalError::new(format!(
878 "cannot check membership of {} in {}",
879 key.type_name(),
880 v.type_name()
881 ))),
882 }
883}
884
885fn trim_str(input: &Value, arg: &Expr, left: bool) -> Result<Vec<Value>, EvalError> {
886 let s = match input {
887 Value::Str(s) => s,
888 other => {
889 return Err(EvalError::new(format!(
890 "expected a string, got {}",
891 other.type_name()
892 )));
893 }
894 };
895 let mut out = Vec::new();
896 for prefix in eval(arg, input)? {
897 let p = match &prefix {
898 Value::Str(p) => p,
899 other => {
900 return Err(EvalError::new(format!(
901 "expected a string argument, got {}",
902 other.type_name()
903 )));
904 }
905 };
906 let trimmed = if left {
907 s.strip_prefix(p).unwrap_or(s)
908 } else {
909 s.strip_suffix(p).unwrap_or(s)
910 };
911 out.push(Value::Str(trimmed.to_string()));
912 }
913 Ok(out)
914}
915
916#[cfg(test)]
917mod tests {
918 use super::*;
919 use crate::parser::parse;
920
921 fn obj(pairs: &[(&str, Value)]) -> Value {
922 Value::Object(
923 pairs
924 .iter()
925 .map(|(k, v)| (k.to_string(), v.clone()))
926 .collect(),
927 )
928 }
929
930 fn run(expr: &str, input: &Value) -> Vec<Value> {
931 eval(&parse(expr).unwrap(), input).unwrap_or_else(|e| panic!("eval `{expr}`: {e}"))
932 }
933
934 fn one(expr: &str, input: &Value) -> Value {
935 let r = run(expr, input);
936 assert_eq!(r.len(), 1, "`{expr}` should yield one value, got {r:?}");
937 r.into_iter().next().unwrap()
938 }
939
940 #[test]
941 fn navigation() {
942 let doc = obj(&[(
943 "compilerOptions",
944 obj(&[
945 ("strict", Value::Bool(true)),
946 ("target", Value::Str("ES2020".into())),
947 ]),
948 )]);
949 assert_eq!(one(".compilerOptions.strict", &doc), Value::Bool(true));
950 assert_eq!(
951 one(".compilerOptions.target", &doc),
952 Value::Str("ES2020".into())
953 );
954 }
955
956 #[test]
957 fn missing_is_empty_stream() {
958 let doc = obj(&[("a", obj(&[("x", Value::Int(1))]))]);
961 assert!(run(".nope", &doc).is_empty());
962 assert!(run(".a.nope", &doc).is_empty());
963 assert!(run(".a.nope.deeper", &doc).is_empty());
964 }
965
966 #[test]
967 fn explicit_null_is_a_value() {
968 let doc = obj(&[("a", Value::Null)]);
969 assert_eq!(run(".a", &doc), vec![Value::Null]);
970 }
971
972 #[test]
973 fn iterate_and_index() {
974 let doc = obj(&[(
975 "lib",
976 Value::Array(vec![Value::Str("ES2020".into()), Value::Str("DOM".into())]),
977 )]);
978 assert_eq!(
979 run(".lib[]", &doc),
980 vec![Value::Str("ES2020".into()), Value::Str("DOM".into())]
981 );
982 assert_eq!(one(".lib[0]", &doc), Value::Str("ES2020".into()));
983 assert_eq!(one(".lib[-1]", &doc), Value::Str("DOM".into()));
984 assert!(run(".lib[9]", &doc).is_empty());
985 }
986
987 #[test]
988 fn select_filter() {
989 let doc = obj(&[(
990 "items",
991 Value::Array(vec![
992 obj(&[
993 ("name", Value::Str("keep".into())),
994 ("on", Value::Bool(true)),
995 ]),
996 obj(&[
997 ("name", Value::Str("drop".into())),
998 ("on", Value::Bool(false)),
999 ]),
1000 ]),
1001 )]);
1002 let r = run(".items[] | select(.on == true)", &doc);
1003 assert_eq!(r.len(), 1);
1004 assert_eq!(one(".name", &r[0]), Value::Str("keep".into()));
1005 }
1006
1007 #[test]
1008 fn arithmetic_and_strings() {
1009 let doc = obj(&[
1010 ("count", Value::Int(5)),
1011 ("name", Value::Str("edikt".into())),
1012 ]);
1013 assert_eq!(one(".count + 1", &doc), Value::Int(6));
1014 assert_eq!(one(".count * 2 - 3", &doc), Value::Int(7));
1015 assert_eq!(one(".name + \"!\"", &doc), Value::Str("edikt!".into()));
1016 assert_eq!(
1017 one(".name | ascii_upcase", &doc),
1018 Value::Str("EDIKT".into())
1019 );
1020 assert_eq!(one(".name | length", &doc), Value::Int(5));
1021 }
1022
1023 #[test]
1024 fn multi_output_comma() {
1025 let doc = obj(&[("a", Value::Int(1)), ("b", Value::Int(2))]);
1026 assert_eq!(run(".a, .b", &doc), vec![Value::Int(1), Value::Int(2)]);
1027 }
1028
1029 #[test]
1030 fn object_construction() {
1031 let doc = obj(&[("x", Value::Int(5))]);
1032 assert_eq!(
1033 one("{ a: 1, b: .x }", &doc),
1034 obj(&[("a", Value::Int(1)), ("b", Value::Int(5))])
1035 );
1036 assert_eq!(one("{}", &doc), Value::Object(vec![]));
1037 }
1038
1039 #[test]
1040 fn bracket_string_keys() {
1041 let doc = obj(&[("weird.key", Value::Str("w".into()))]);
1042 assert_eq!(one(r#".["weird.key"]"#, &doc), Value::Str("w".into()));
1043 }
1044
1045 #[test]
1046 fn builtins() {
1047 let doc = obj(&[("a", Value::Int(1)), ("b", Value::Int(2))]);
1048 assert_eq!(
1049 one("keys", &doc),
1050 Value::Array(vec![Value::Str("a".into()), Value::Str("b".into())])
1051 );
1052 assert_eq!(one("has(\"a\")", &doc), Value::Bool(true));
1053 assert_eq!(one("type", &doc), Value::Str("object".into()));
1054 assert_eq!(one("length", &doc), Value::Int(2));
1055 assert_eq!(one("\"12\" | tonumber", &Value::Null), Value::Int(12));
1056 assert_eq!(
1057 one("\"pre-x\" | ltrimstr(\"pre-\")", &Value::Null),
1058 Value::Str("x".into())
1059 );
1060 }
1061
1062 #[test]
1063 fn type_errors() {
1064 assert!(eval(&parse(".a").unwrap(), &Value::Int(3)).is_err());
1065 assert!(eval(&parse(".[]").unwrap(), &Value::Int(3)).is_err());
1066 assert!(eval(&parse("length").unwrap(), &Value::Int(3)).is_err());
1067 }
1068
1069 #[test]
1070 fn value_level_set_paths() {
1071 let r = run(".a.b.c = 1", &Value::Null);
1073 assert_eq!(one(".a.b.c", &r[0]), Value::Int(1));
1074 let arr = run(
1075 ".xs[2] = 9",
1076 &obj(&[("xs", Value::Array(vec![Value::Int(0)]))]),
1077 );
1078 assert_eq!(
1079 one(".xs", &arr[0]),
1080 Value::Array(vec![Value::Int(0), Value::Null, Value::Int(9)])
1081 );
1082 let it = run(".[] = 0", &Value::Array(vec![Value::Int(1), Value::Int(2)]));
1084 assert_eq!(it[0], Value::Array(vec![Value::Int(0), Value::Int(0)]));
1085 let ito = run(
1086 ".[] = 0",
1087 &obj(&[("a", Value::Int(1)), ("b", Value::Int(2))]),
1088 );
1089 assert_eq!(ito[0], obj(&[("a", Value::Int(0)), ("b", Value::Int(0))]));
1090 assert!(
1092 eval(
1093 &parse(".xs[-9] = 1").unwrap(),
1094 &obj(&[("xs", Value::Array(vec![]))])
1095 )
1096 .is_err()
1097 );
1098 assert!(eval(&parse(".a = 1").unwrap(), &Value::Int(3)).is_err());
1099 assert!(eval(&parse(".[0] = 1").unwrap(), &Value::Str("x".into())).is_err());
1100 }
1101
1102 #[test]
1103 fn value_level_update_and_delete() {
1104 assert_eq!(
1106 one(".a |= . + 1", &obj(&[("a", Value::Int(1))])),
1107 obj(&[("a", Value::Int(2))])
1108 );
1109 let xs = obj(&[("xs", Value::Array(vec![Value::Int(1), Value::Int(2)]))]);
1110 assert_eq!(
1111 one(".xs[0] |= . * 10", &xs),
1112 obj(&[("xs", Value::Array(vec![Value::Int(10), Value::Int(2)]))])
1113 );
1114 assert_eq!(
1115 one(".xs[] |= . + 1", &xs),
1116 obj(&[("xs", Value::Array(vec![Value::Int(2), Value::Int(3)]))])
1117 );
1118 assert_eq!(
1120 one(
1121 "del(.a.b)",
1122 &obj(&[("a", obj(&[("b", Value::Int(1)), ("c", Value::Int(2))]))])
1123 ),
1124 obj(&[("a", obj(&[("c", Value::Int(2))]))])
1125 );
1126 assert_eq!(
1127 one("del(.xs[0])", &xs),
1128 obj(&[("xs", Value::Array(vec![Value::Int(2)]))])
1129 );
1130 assert_eq!(one("del(.xs[])", &xs), obj(&[("xs", Value::Array(vec![]))]));
1131 assert_eq!(
1132 one("del(.nope)", &obj(&[("a", Value::Int(1))])),
1133 obj(&[("a", Value::Int(1))])
1134 );
1135 assert!(eval(&parse(".a |= .").unwrap(), &Value::Int(3)).is_err());
1137 }
1138
1139 #[test]
1140 fn arithmetic_and_its_errors() {
1141 assert_eq!(one("3 - 1", &Value::Null), Value::Int(2));
1142 assert_eq!(one("3 * 4", &Value::Null), Value::Int(12));
1143 assert_eq!(one("7 % 3", &Value::Null), Value::Int(1));
1144 assert_eq!(one("6 / 2", &Value::Null), Value::Int(3)); assert_eq!(one("7 / 2", &Value::Null), Value::Float(3.5)); assert_eq!(one("2.5 + 0.5", &Value::Null), Value::Int(3)); assert_eq!(one("1 < 2", &Value::Null), Value::Bool(true));
1149 assert_eq!(one("2 <= 2", &Value::Null), Value::Bool(true));
1150 assert_eq!(one("3 >= 4", &Value::Null), Value::Bool(false));
1151 assert_eq!(one("1 != 2", &Value::Null), Value::Bool(true));
1152 assert!(eval(&parse("1 / 0").unwrap(), &Value::Null).is_err());
1154 assert!(eval(&parse("1 % 0").unwrap(), &Value::Null).is_err());
1155 assert!(eval(&parse("\"a\" - 1").unwrap(), &Value::Null).is_err());
1156 assert!(eval(&parse("-\"a\"").unwrap(), &Value::Null).is_err());
1157 assert!(matches!(
1159 one("9223372036854775807 + 1", &Value::Null),
1160 Value::Float(_)
1161 ));
1162 }
1163
1164 #[test]
1165 fn add_is_overloaded() {
1166 assert_eq!(one("null + 5", &Value::Null), Value::Int(5));
1167 assert_eq!(one("5 + null", &Value::Null), Value::Int(5));
1168 assert_eq!(one("\"a\" + \"b\"", &Value::Null), Value::Str("ab".into()));
1169 assert_eq!(
1170 one("[1] + [2]", &Value::Null),
1171 Value::Array(vec![Value::Int(1), Value::Int(2)])
1172 );
1173 }
1174
1175 #[test]
1176 fn builtin_error_and_edge_paths() {
1177 assert!(eval(&parse("has(\"a\")").unwrap(), &Value::Int(1)).is_err());
1179 assert!(eval(&parse("keys").unwrap(), &Value::Int(1)).is_err());
1180 assert_eq!(
1181 one("has(1)", &Value::Array(vec![Value::Int(0), Value::Int(0)])),
1182 Value::Bool(true)
1183 );
1184 assert_eq!(
1185 one("\"abc\" | ltrimstr(\"x\")", &Value::Null),
1186 Value::Str("abc".into())
1187 );
1188 assert_eq!(
1189 one("\"abc\" | rtrimstr(\"bc\")", &Value::Null),
1190 Value::Str("a".into())
1191 );
1192 assert_eq!(one("\"42\" | tonumber", &Value::Null), Value::Int(42));
1193 assert!(eval(&parse("\"x\" | tonumber").unwrap(), &Value::Null).is_err());
1194 assert_eq!(one("length", &Value::Str("héllo".into())), Value::Int(5));
1195 assert_eq!(one("length", &Value::Null), Value::Int(0));
1196 assert!(eval(&parse("nope").unwrap(), &Value::Null).is_err());
1198 assert!(eval(&parse("length(1)").unwrap(), &Value::Null).is_err());
1199 }
1200
1201 #[test]
1202 fn alternative_operator() {
1203 let doc = obj(&[
1204 ("a", Value::Int(1)),
1205 ("z", Value::Null),
1206 ("f", Value::Bool(false)),
1207 ]);
1208 assert_eq!(one(r#".a // "d""#, &doc), Value::Int(1));
1210 assert_eq!(one(r#".nope // "d""#, &doc), Value::Str("d".into()));
1212 assert_eq!(one(r#".z // "d""#, &doc), Value::Str("d".into()));
1213 assert_eq!(one(r#".f // "d""#, &doc), Value::Str("d".into()));
1214 assert_eq!(
1216 one(r#".x // .y // "last""#, &doc),
1217 Value::Str("last".into())
1218 );
1219 let r = run(r#".k = .nope // "d""#, &doc);
1221 assert_eq!(one(".k", &r[0]), Value::Str("d".into()));
1222 assert_eq!(one(r#".a == 2 // "d""#, &doc), Value::Str("d".into()));
1224 let items = obj(&[(
1226 "xs",
1227 Value::Array(vec![Value::Bool(false), Value::Int(7), Value::Null]),
1228 )]);
1229 assert_eq!(run(r#".xs[] // "d""#, &items), vec![Value::Int(7)]);
1230 assert!(eval(&parse(r#".a.b // "d""#).unwrap(), &doc).is_err());
1233 }
1234
1235 #[test]
1236 fn comments_stream_records_and_paths() {
1237 use crate::comment::{Commented, CommentedNode, Comments};
1238 let img = Commented {
1240 comments: Comments {
1241 head: vec![],
1242 inline: Some("pinned".into()),
1243 foot: vec![],
1244 },
1245 node: CommentedNode::Scalar(Value::Str("nginx".into())),
1246 };
1247 let web = Commented {
1248 comments: Comments {
1249 head: vec!["the service".into()],
1250 inline: None,
1251 foot: vec![],
1252 },
1253 node: CommentedNode::Object(vec![("image".into(), img)]),
1254 };
1255 let debug = Commented {
1256 comments: Comments {
1257 head: vec![],
1258 inline: Some("TODO remove".into()),
1259 foot: vec![],
1260 },
1261 node: CommentedNode::Scalar(Value::Bool(false)),
1262 };
1263 let root = Commented {
1264 comments: Comments::default(),
1265 node: CommentedNode::Object(vec![("web".into(), web), ("debug".into(), debug)]),
1266 };
1267
1268 let recs = comment_records(&root);
1270 assert_eq!(recs.len(), 3);
1271 let todos = eval_with_comments(
1273 &parse(r#"comments | select(.text | test("TODO")) | .path"#).unwrap(),
1274 &root,
1275 )
1276 .unwrap();
1277 assert_eq!(todos, vec![Value::Str(".debug".into())]);
1278 let paths = eval_with_comments(&parse("comments | .path").unwrap(), &root).unwrap();
1280 assert_eq!(
1281 paths,
1282 vec![
1283 Value::Str(".web".into()),
1284 Value::Str(".web.image".into()),
1285 Value::Str(".debug".into()),
1286 ]
1287 );
1288 assert_eq!(
1290 eval_with_comments(&parse("[comments] | length").unwrap(), &root).unwrap(),
1291 vec![Value::Int(3)]
1292 );
1293 }
1294
1295 #[test]
1296 fn regex_test_match_capture() {
1297 let s = Value::Str("nginx:1.25".into());
1298 assert_eq!(one(r#"test("^nginx")"#, &s), Value::Bool(true));
1299 assert_eq!(one(r#"test("^NGINX")"#, &s), Value::Bool(false));
1300 assert_eq!(one(r#"test("^NGINX"; "i")"#, &s), Value::Bool(true));
1302
1303 assert!(run(r#"match("\\d+"; "g")"#, &Value::Str("a1b22".into())).len() == 2);
1306 assert!(run(r#"match("z")"#, &s).is_empty());
1307 let m = one(r#"match(":(\\d+)")"#, &s);
1308 assert_eq!(one(".offset", &m), Value::Int(5));
1309 assert_eq!(one(".string", &m), Value::Str(":1".into()));
1310 assert_eq!(one(".captures[0].string", &m), Value::Str("1".into()));
1311
1312 assert_eq!(
1314 one(r#"capture("(?<img>\\w+):(?<tag>.+)")"#, &s),
1315 obj(&[
1316 ("img", Value::Str("nginx".into())),
1317 ("tag", Value::Str("1.25".into())),
1318 ])
1319 );
1320
1321 assert!(eval(&parse(r#"test("(")"#).unwrap(), &s).is_err());
1323 assert!(eval(&parse(r#"test("a"; "q")"#).unwrap(), &s).is_err());
1324 assert!(eval(&parse(r#"test("a")"#).unwrap(), &Value::Int(1)).is_err());
1325 }
1326
1327 #[test]
1328 fn regex_sub_and_gsub() {
1329 let v = Value::Str("v1.2.3".into());
1330 assert_eq!(one(r#"sub("^v"; "")"#, &v), Value::Str("1.2.3".into()));
1331 let s = Value::Str("a-b-c".into());
1333 assert_eq!(one(r#"sub("-"; "_")"#, &s), Value::Str("a_b-c".into()));
1334 assert_eq!(one(r#"gsub("-"; "_")"#, &s), Value::Str("a_b_c".into()));
1335 assert_eq!(
1336 one(
1337 r#"sub("(?<k>\\w+)=(?<v>\\w+)"; "${v}:${k}")"#,
1338 &Value::Str("port=80".into())
1339 ),
1340 Value::Str("80:port".into())
1341 );
1342 }
1343
1344 #[test]
1345 fn split_join_and_affixes() {
1346 let path = Value::Str("/usr/bin:/bin".into());
1347 assert_eq!(
1348 one(r#"split(":")"#, &path),
1349 Value::Array(vec![
1350 Value::Str("/usr/bin".into()),
1351 Value::Str("/bin".into()),
1352 ])
1353 );
1354 assert_eq!(
1356 one(r#"split(":") + ["/sbin"] | join(":")"#, &path),
1357 Value::Str("/usr/bin:/bin:/sbin".into())
1358 );
1359 assert_eq!(
1361 one(r#""a1b22c" | split("\\d+"; "")"#, &Value::Null),
1362 Value::Array(vec![
1363 Value::Str("a".into()),
1364 Value::Str("b".into()),
1365 Value::Str("c".into()),
1366 ])
1367 );
1368 assert_eq!(
1369 one(r#""VITE_PORT" | startswith("VITE_")"#, &Value::Null),
1370 Value::Bool(true)
1371 );
1372 assert_eq!(
1373 one(r#""app.log" | endswith(".log")"#, &Value::Null),
1374 Value::Bool(true)
1375 );
1376 assert!(eval(&parse(r#"join(",")"#).unwrap(), &Value::Str("x".into())).is_err());
1378 }
1379
1380 #[test]
1383 fn assign_sets_and_leaves_siblings() {
1384 let doc = obj(&[("a", Value::Int(1)), ("b", Value::Int(2))]);
1385 let r = run(".a = 5", &doc);
1386 assert_eq!(one(".a", &r[0]), Value::Int(5));
1387 assert_eq!(one(".b", &r[0]), Value::Int(2));
1388 }
1389
1390 #[test]
1391 fn assign_creates_missing_key() {
1392 let doc = obj(&[("a", Value::Int(1))]);
1393 let r = run(".c = 9", &doc);
1394 assert_eq!(one(".c", &r[0]), Value::Int(9));
1395 }
1396
1397 #[test]
1398 fn assign_rhs_evaluated_against_input() {
1399 let doc = obj(&[("a", Value::Int(1)), ("b", Value::Int(7))]);
1400 let r = run(".a = .b", &doc);
1401 assert_eq!(one(".a", &r[0]), Value::Int(7));
1402 }
1403
1404 #[test]
1405 fn assign_into_array_index() {
1406 let doc = obj(&[("a", Value::Array(vec![Value::Int(1), Value::Int(2)]))]);
1407 let r = run(".a[0] = 9", &doc);
1408 assert_eq!(one(".a[0]", &r[0]), Value::Int(9));
1409 assert_eq!(one(".a[1]", &r[0]), Value::Int(2));
1410 }
1411
1412 #[test]
1413 fn update_assign_computes_and_maps() {
1414 let doc = obj(&[
1415 ("count", Value::Int(5)),
1416 ("name", Value::Str("edikt".into())),
1417 ]);
1418 let r = run(".count |= . + 1", &doc);
1419 assert_eq!(one(".count", &r[0]), Value::Int(6));
1420 let r2 = run(".name |= ascii_upcase", &doc);
1421 assert_eq!(one(".name", &r2[0]), Value::Str("EDIKT".into()));
1422 }
1423
1424 #[test]
1425 fn mutation_detection() {
1426 assert!(parse(".a = 1").unwrap().is_mutation());
1427 assert!(parse(".a |= . + 1").unwrap().is_mutation());
1428 assert!(parse("del(.a)").unwrap().is_mutation());
1429 assert!(!parse(".a.b").unwrap().is_mutation());
1430 assert!(!parse(".items[] | select(. == 1)").unwrap().is_mutation());
1431 }
1432
1433 #[test]
1434 fn assign_lhs_must_be_path() {
1435 assert!(eval(&parse("1 = 2").unwrap(), &Value::Null).is_err());
1437 }
1438
1439 #[test]
1440 fn del_removes_key_and_index() {
1441 let doc = obj(&[("a", Value::Int(1)), ("b", Value::Int(2))]);
1442 let r = run("del(.a)", &doc);
1443 assert!(run(".a", &r[0]).is_empty());
1444 assert_eq!(one(".b", &r[0]), Value::Int(2));
1445
1446 let arr = obj(&[(
1447 "x",
1448 Value::Array(vec![Value::Int(10), Value::Int(20), Value::Int(30)]),
1449 )]);
1450 let r2 = run("del(.x[1])", &arr);
1451 assert_eq!(run(".x[]", &r2[0]), vec![Value::Int(10), Value::Int(30)]);
1452 }
1453
1454 #[test]
1455 fn del_missing_is_noop() {
1456 let doc = obj(&[("a", Value::Int(1))]);
1457 let r = run("del(.nope)", &doc);
1458 assert_eq!(one(".a", &r[0]), Value::Int(1));
1459 }
1460
1461 #[test]
1462 fn del_nested() {
1463 let doc = obj(&[("a", obj(&[("b", Value::Int(1)), ("c", Value::Int(2))]))]);
1464 let r = run("del(.a.b)", &doc);
1465 assert!(run(".a.b", &r[0]).is_empty());
1466 assert_eq!(one(".a.c", &r[0]), Value::Int(2));
1467 }
1468
1469 #[test]
1470 fn add_assign_number_string_array() {
1471 let doc = obj(&[
1472 ("count", Value::Int(5)),
1473 ("name", Value::Str("edikt".into())),
1474 ("list", Value::Array(vec![Value::Int(1)])),
1475 ]);
1476 assert_eq!(one(".count", &run(".count += 3", &doc)[0]), Value::Int(8));
1477 assert_eq!(
1478 one(".name", &run(".name += \"!\"", &doc)[0]),
1479 Value::Str("edikt!".into())
1480 );
1481 let appended = run(".list += [2, 3]", &doc);
1482 assert_eq!(
1483 run(".list[]", &appended[0]),
1484 vec![Value::Int(1), Value::Int(2), Value::Int(3)]
1485 );
1486 }
1487
1488 #[test]
1489 fn add_assign_null_identity() {
1490 let doc = obj(&[("a", Value::Int(1))]);
1492 let r = run(".tags += [\"x\"]", &doc);
1493 assert_eq!(run(".tags[]", &r[0]), vec![Value::Str("x".into())]);
1494 }
1495}