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