1#![allow(unused_variables)]
2use crate::builtins::BUILTINS;
3use crate::fromnow::{from_now, now};
4use crate::interpreter::{self, Context};
5use crate::op_props::{parse_by, parse_each, parse_each_three};
6use crate::value::{Object, Value};
7use anyhow::{bail, Result};
8use nom::{
9 branch::alt,
10 bytes::complete::tag,
11 character::complete::{alpha1, alphanumeric1},
12 combinator::{all_consuming, recognize},
13 multi::many0,
14 sequence::pair,
15};
16use serde_json::Value as SerdeValue;
17use std::borrow::Cow;
18use std::convert::TryInto;
19use std::fmt::Write;
20
21pub fn render(template: &SerdeValue, context: &SerdeValue) -> Result<SerdeValue> {
23 let template: Value = template.into();
24 let context = Context::from_serde_value(context, Some(&BUILTINS))?;
25
26 let mut context = context.child();
28 context.insert("now", Value::String(now()));
29
30 match _render(&template, &context) {
31 Ok(v) => Ok(v.try_into()?),
33 Err(e) => Err(e),
34 }
35}
36
37fn _render(template: &Value, context: &Context) -> Result<Value> {
39 fn render_or_deletion_marker(v: &Value, context: &Context) -> Option<Result<Value>> {
42 match _render(v, context) {
43 Ok(Value::DeletionMarker) => None,
44 Ok(rendered) => Some(Ok(rendered)),
45 Err(e) => Some(Err(e)),
46 }
47 }
48
49 Ok(match template {
50 Value::Number(_) | Value::Bool(_) | Value::Null => (*template).clone(),
51 Value::String(s) => Value::String(interpolate(s, context)?),
52 Value::Array(elements) => Value::Array(
53 elements
54 .into_iter()
55 .filter_map(|e| render_or_deletion_marker(e, context))
56 .collect::<Result<Vec<Value>>>()?,
57 ),
58 Value::Object(o) => {
59 for (k, v) in o.iter() {
61 let interpolated = interpolate(&k, context)?;
64 let mut chars = interpolated.chars();
65 if chars.next() == Some('$') && chars.next() != Some('$') {
66 if let Some(rendered) = maybe_operator(k, v, o, context)? {
67 return Ok(rendered);
68 }
69 }
70 }
71
72 let mut result = Object::new();
74 for (k, v) in o.iter() {
75 let k = if k.starts_with("$$") { &k[1..] } else { &k[..] };
77 match _render(v, context)? {
78 Value::DeletionMarker => {}
79 v => {
80 result.insert(interpolate(k, context)?, v);
81 }
82 };
83 }
84 Value::Object(result)
85 }
86
87 Value::DeletionMarker | Value::Function(_) => unreachable!(),
89 })
90}
91
92fn interpolate(mut source: &str, context: &Context) -> Result<String> {
94 if source.find('$') == None {
96 return Ok(source.into());
97 }
98
99 let mut result = String::new();
100
101 while source.len() > 0 {
102 if let Some(offset) = source.find('$') {
103 if let Some(s) = source.get(offset..offset + 2) {
105 if s == "${" {
106 result.push_str(source.get(..offset).unwrap());
107 let expr = source.get(offset + 2..).unwrap();
108 let (parsed, remainder) = interpreter::parse_partial(expr)?;
109 if remainder.get(0..1) != Some("}") {
110 let msg = "unterminated ${..} expression";
112 bail!(msg);
113 }
114 let eval_result = interpreter::evaluate(&parsed, context)?;
115
116 match eval_result {
117 Value::Number(n) => write!(&mut result, "{}", n)?,
118 Value::Bool(true) => result.push_str("true"),
119 Value::Bool(false) => result.push_str("false"),
120 Value::Null => {}
122 Value::String(s) => result.push_str(&s),
123 _ => bail!("interpolation of '{}' produced an array or object", expr),
124 }
125
126 source = &remainder[1..];
127 continue;
128 }
129 }
130
131 if let Some(s) = source.get(offset..offset + 3) {
133 if s == "$${" {
134 result.push_str(source.get(..offset + 1).unwrap());
135 source = source.get(offset + 2..).unwrap();
136 continue;
137 }
138 }
139
140 result.push_str(source.get(..offset + 1).unwrap());
142 source = source.get(offset + 1..).unwrap();
143 } else {
144 result.push_str(source);
146 source = "";
147 }
148 }
149
150 Ok(result)
151}
152
153fn evaluate(expression: &str, context: &Context) -> Result<Value> {
155 let parsed = interpreter::parse_all(expression)?;
156 interpreter::evaluate(&parsed, context).map(|v| v.into())
157}
158
159fn maybe_operator(
163 operator: &str,
164 value: &Value,
165 object: &Object,
166 context: &Context,
167) -> Result<Option<Value>> {
168 match operator {
169 "$eval" => Ok(Some(eval_operator(operator, value, object, context)?)),
170 "$flatten" => Ok(Some(flatten_operator(operator, value, object, context)?)),
171 "$flattenDeep" => Ok(Some(flatten_deep_operator(
172 operator, value, object, context,
173 )?)),
174 "$fromNow" => Ok(Some(from_now_operator(operator, value, object, context)?)),
175 "$if" => Ok(Some(if_operator(operator, value, object, context)?)),
176 "$json" => Ok(Some(json_operator(operator, value, object, context)?)),
177 "$let" => Ok(Some(let_operator(operator, value, object, context)?)),
178 "$map" => Ok(Some(map_operator(operator, value, object, context)?)),
179 "$reduce" => Ok(Some(reduce_operator(operator, value, object, context)?)),
180 "$find" => Ok(Some(find_operator(operator, value, object, context)?)),
181 "$match" => Ok(Some(match_operator(operator, value, object, context)?)),
182 "$switch" => Ok(Some(switch_operator(operator, value, object, context)?)),
183 "$merge" => Ok(Some(merge_operator(operator, value, object, context)?)),
184 "$mergeDeep" => Ok(Some(merge_deep_operator(operator, value, object, context)?)),
185 "$reverse" => Ok(Some(reverse_operator(operator, value, object, context)?)),
186 "$sort" => Ok(Some(sort_operator(operator, value, object, context)?)),
187
188 _ => Err(template_error!(
190 "$<identifier> is reserved; use $$<identifier> ({})",
191 operator
192 )),
193 }
194}
195
196#[inline(always)]
199fn check_operator_properties<F>(operator: &str, object: &Object, check: F) -> Result<()>
200where
201 F: Fn(&str) -> bool,
202{
203 if object.len() == 1 {
205 return Ok(());
206 }
207
208 let mut unknown = Vec::new();
210
211 for (k, _) in object.iter() {
212 if k == operator {
213 continue;
214 }
215 if !check(k) {
216 unknown.push(k.as_ref());
217 }
218 }
219
220 if unknown.len() > 0 {
221 unknown.sort();
222 Err(template_error!(
223 "{} has undefined properties: {}",
224 operator,
225 unknown.join(" ")
226 ))?;
227 }
228
229 Ok(())
230}
231
232fn eval_operator(
233 operator: &str,
234 value: &Value,
235 object: &Object,
236 context: &Context,
237) -> Result<Value> {
238 check_operator_properties(operator, object, |_| false)?;
239 if let Value::String(expr) = value {
240 Ok(evaluate(expr, context)?)
241 } else {
242 Err(template_error!("$eval must be given a string expression"))
243 }
244}
245
246fn flatten_operator(
247 operator: &str,
248 value: &Value,
249 object: &Object,
250 context: &Context,
251) -> Result<Value> {
252 check_operator_properties(operator, object, |_| false)?;
253 if let Value::Array(ref mut items) = _render(value, context)? {
254 let mut resitems = Vec::new();
255 for mut item in items.drain(..) {
256 if let Value::Array(ref mut subitems) = item {
257 for subitem in subitems.drain(..) {
258 resitems.push(subitem);
259 }
260 } else {
261 resitems.push(item);
262 }
263 }
264 Ok(Value::Array(resitems))
265 } else {
266 Err(template_error!("$flatten value must evaluate to an array"))
267 }
268}
269
270fn flatten_deep_operator(
271 operator: &str,
272 value: &Value,
273 object: &Object,
274 context: &Context,
275) -> Result<Value> {
276 check_operator_properties(operator, object, |_| false)?;
277
278 fn flatten_deep(mut value: Value, accumulator: &mut Vec<Value>) {
279 if let Value::Array(ref mut items) = value {
280 for item in items.drain(..) {
281 flatten_deep(item, accumulator);
282 }
283 } else {
284 accumulator.push(value);
285 }
286 }
287
288 if let value @ Value::Array(_) = _render(value, context)? {
289 let mut resitems = Vec::new();
290 flatten_deep(value, &mut resitems);
291 Ok(Value::Array(resitems))
292 } else {
293 Err(template_error!("$flatten value must evaluate to an array"))
294 }
295}
296
297fn from_now_operator(
298 operator: &str,
299 value: &Value,
300 object: &Object,
301 context: &Context,
302) -> Result<Value> {
303 check_operator_properties(operator, object, |prop| prop == "from")?;
304 let reference: Cow<str>;
305
306 if let Some(val) = object.get("from") {
308 match _render(val, context)? {
309 Value::String(ref s) => {
310 reference = Cow::Owned(s.to_string());
311 }
312 _ => {
313 return Err(template_error!("$fromNow expects a string"));
314 }
315 };
316 } else {
317 match context.get("now") {
319 None => unreachable!(), Some(Value::String(ref s)) => reference = Cow::Borrowed(s),
321 _ => return Err(template_error!("context value `now` must be a string")),
322 };
323 }
324
325 match _render(value, context)? {
326 Value::String(s) => Ok(Value::String(from_now(&s, reference.as_ref())?)),
327 _ => Err(template_error!("$fromNow expects a string")),
328 }
329}
330
331fn if_operator(operator: &str, value: &Value, object: &Object, context: &Context) -> Result<Value> {
332 check_operator_properties(operator, object, |prop| prop == "then" || prop == "else")?;
333
334 let eval_result = match value {
335 Value::String(s) => evaluate(&s, context)?,
336 _ => return Err(template_error!("$if can evaluate string expressions only")),
337 };
338
339 let prop = if eval_result.into() { "then" } else { "else" };
340 match object.get(prop) {
341 None => Ok(Value::DeletionMarker),
342 Some(val) => Ok(_render(val, context)?),
343 }
344}
345
346fn json_operator(
347 operator: &str,
348 value: &Value,
349 object: &Object,
350 context: &Context,
351) -> Result<Value> {
352 check_operator_properties(operator, object, |_| false)?;
353 let v = _render(value, context)?;
354 Ok(Value::String(v.to_json()?))
355}
356
357fn let_operator(
358 operator: &str,
359 value: &Value,
360 object: &Object,
361 context: &Context,
362) -> Result<Value> {
363 check_operator_properties(operator, object, |p| p == "in")?;
364
365 if !value.is_object() {
366 return Err(template_error!("$let value must be an object"));
367 }
368
369 let value = _render(value, context)?;
370
371 if let Value::Object(o) = value {
372 let mut child_context = context.child();
373 for (k, v) in o.iter() {
374 if !is_identifier(k) {
375 return Err(template_error!(
376 "top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/"
377 ));
378 }
379 child_context.insert(k, v.clone());
380 }
381
382 if let Some(in_tpl) = object.get("in") {
383 Ok(_render(in_tpl, &child_context)?)
384 } else {
385 Err(template_error!("$let operator requires an `in` clause"))
386 }
387 } else {
388 Err(template_error!("$let value must be an object"))
389 }
390}
391
392fn map_operator(
393 operator: &str,
394 value: &Value,
395 object: &Object,
396 context: &Context,
397) -> Result<Value> {
398 check_operator_properties(operator, object, |p| parse_each(p).is_some())?;
399 if object.len() != 2 {
400 return Err(template_error!("$map must have exactly two properties"));
401 }
402
403 let each_prop = object.keys().filter(|k| k != &"$map").next().unwrap();
405
406 let (value_var, index_var) = parse_each(each_prop)
407 .ok_or_else(|| template_error!("$map requires each(identifier[,identifier]) syntax"))?;
408
409 let each_tpl = object.get(each_prop).unwrap();
410
411 let mut value = _render(value, context)?;
412
413 match value {
414 Value::Object(ref o) => {
415 let mut result = Object::new();
416
417 for (k, v) in o.iter() {
418 let mut subcontext = context.child();
419
420 if let Some(index_var) = index_var {
421 subcontext.insert(index_var, Value::String(k.to_string()));
423 subcontext.insert(value_var, v.clone());
424 } else {
425 let mut arg = Object::new();
427 arg.insert("key".to_string(), Value::String(k.to_string()));
428 arg.insert("val".to_string(), v.clone());
429 subcontext.insert(value_var, Value::Object(arg));
430 }
431
432 let rendered = _render(each_tpl, &subcontext)?;
433
434 if let Value::Object(r) = rendered {
435 for (rk, rv) in r {
436 result.insert(rk, rv);
437 }
438 } else {
439 return Err(template_error!(
440 "$map on objects expects each(..) to evaluate to an object"
441 ));
442 }
443 }
444 Ok(Value::Object(result))
445 }
446 Value::Array(ref mut a) => {
447 let mapped = a
448 .drain(..)
449 .enumerate()
450 .map(|(i, v)| {
451 let mut subcontext = context.child();
452 subcontext.insert(value_var, v);
453 if let Some(index_var) = index_var {
454 subcontext.insert(index_var, Value::Number(i as f64));
455 }
456 _render(each_tpl, &subcontext)
457 })
458 .filter(|v| match v {
459 Ok(Value::DeletionMarker) => false,
460 _ => true,
461 })
462 .collect::<Result<Vec<_>>>()?;
463 Ok(Value::Array(mapped))
464 }
465 _ => Err(template_error!(
466 "$map value must evaluate to an array or object"
467 )),
468 }
469}
470
471fn reduce_operator(
472 operator: &str,
473 value: &Value,
474 object: &Object,
475 context: &Context,
476) -> Result<Value> {
477 check_operator_properties(operator, object, |p| p == "initial" || parse_each_three(p).is_some())?;
478 if object.len() != 3 {
479 return Err(template_error!("$reduce must have exactly three properties"));
480 }
481
482 let each_prop = object.keys().filter(|k| k != &"$reduce" && k != &"initial").next().unwrap();
484
485 let (acc_var, value_var, index_var) = parse_each_three(each_prop)
486 .ok_or_else(|| template_error!("$reduce requires each(identifier,identifier[,identifier]) syntax"))?;
487
488 let each_tpl = object.get(each_prop).unwrap();
489
490 let mut value = _render(value, context)?;
491 let initial = object.get("initial").unwrap();
493
494 match value {
495 Value::Array(ref mut a) => {
496 let mapped = a
497 .drain(..)
498 .enumerate()
499 .try_fold(initial.clone(), |acc, (i, v)| {
500 let mut subcontext = context.child();
501 subcontext.insert(acc_var, acc.clone());
502 subcontext.insert(value_var, v);
503 if let Some(index_var) = index_var {
504 subcontext.insert(index_var, Value::Number(i as f64));
505 }
506 let rendered = _render(each_tpl, &subcontext);
507 match rendered {
508 Ok(Value::DeletionMarker) => Ok(acc),
509 Ok(v) => Ok(v),
510 Err(e) => Err(e),
511 }
512 });
513 mapped
514 }
515 _ => Err(template_error!(
516 "$reduce value must evaluate to an array"
517 )),
518 }
519}
520
521fn find_operator(
522 operator: &str,
523 value: &Value,
524 object: &Object,
525 context: &Context,
526) -> Result<Value> {
527 check_operator_properties(operator, object, |p| parse_each(p).is_some())?;
528 if object.len() != 2 {
529 return Err(template_error!("$find must have exactly two properties"));
530 }
531
532 let each_prop = object.keys().filter(|k| k != &"$find").next().unwrap();
534
535 let (value_var, index_var) = parse_each(each_prop)
536 .ok_or_else(|| template_error!("$find requires each(identifier[,identifier]) syntax"))?;
537
538 let each_tpl = object.get(each_prop).unwrap();
539
540 let mut value = _render(value, context)?;
541
542 if let Value::Array(ref mut a) = value {
543 for (i, v) in a.iter().enumerate() {
544 let mut subcontext = context.child();
545 subcontext.insert(value_var, v.clone());
546 if let Some(index_var) = index_var {
547 subcontext.insert(index_var, Value::Number(i as f64));
548 }
549
550 if let Value::String(ref s) = each_tpl {
551 let eval_result = evaluate(&s, &subcontext)?;
552 if bool::from(eval_result) {
553 return Ok(_render(&v, &subcontext)?);
554 }
555 } else {
556 return Err(template_error!("$find can evaluate string expressions only"));
557 }
558 }
559 Ok(Value::DeletionMarker)
560 } else {
561 Err(template_error!("$find value must be an array"))
562 }
563}
564
565fn match_operator(
566 operator: &str,
567 value: &Value,
568 object: &Object,
569 context: &Context,
570) -> Result<Value> {
571 check_operator_properties(operator, object, |_| false)?;
572 if let Value::Object(ref obj) = value {
573 let mut res = vec![];
574 for (cond, val) in obj {
575 if let Ok(cond) = evaluate(&cond, context) {
576 if !bool::from(cond) {
577 continue;
578 }
579 res.push(_render(val, context)?);
580 } else {
581 bail!(template_error!("parsing error in condition"));
582 }
583 }
584 Ok(Value::Array(res))
585 } else {
586 Err(template_error!("$match can evaluate objects only"))
587 }
588}
589
590fn switch_operator(
591 operator: &str,
592 value: &Value,
593 object: &Object,
594 context: &Context,
595) -> Result<Value> {
596 if let Value::Object(ref obj) = value {
597 let mut res = None;
598 let mut unrendered_default = None;
599 for (cond, val) in obj {
600 if cond == "$default" {
602 unrendered_default = Some(val);
603 continue;
604 }
605 if let Ok(cond) = evaluate(&cond, context) {
607 if !bool::from(cond) {
608 continue;
609 }
610 if res.is_some() {
611 bail!(template_error!(
612 "$switch can only have one truthy condition"
613 ))
614 }
615 res = Some(val);
616 } else {
617 bail!(template_error!("parsing error in condition"));
618 }
619 }
620
621 if let Some(res) = res {
622 _render(res, context)
623 } else if let Some(unrendered_default) = unrendered_default {
624 _render(unrendered_default, context)
625 } else {
626 Ok(Value::DeletionMarker)
627 }
628 } else {
629 Err(template_error!("$switch can evaluate objects only"))
630 }
631}
632
633fn merge_operator(
634 operator: &str,
635 value: &Value,
636 object: &Object,
637 context: &Context,
638) -> Result<Value> {
639 check_operator_properties(operator, object, |_| false)?;
640 if let Value::Array(items) = _render(value, context)? {
641 let mut new_obj = std::collections::BTreeMap::new();
642 for item in items {
643 if let Value::Object(mut obj) = item {
644 new_obj.append(&mut obj);
645 } else {
646 return Err(template_error!(
647 "$merge value must evaluate to an array of objects"
648 ));
649 }
650 }
651 Ok(Value::Object(new_obj))
652 } else {
653 Err(template_error!(
654 "$merge value must evaluate to an array of objects"
655 ))
656 }
657}
658
659fn merge_deep_operator(
660 operator: &str,
661 value: &Value,
662 object: &Object,
663 context: &Context,
664) -> Result<Value> {
665 fn merge_deep(a: &Value, b: &Value) -> Value {
666 match (a, b) {
667 (Value::Array(a), Value::Array(b)) => {
668 let mut a = a.clone();
669 a.append(&mut b.clone());
670 Value::Array(a)
671 }
672 (Value::Object(a), Value::Object(b)) => {
673 let mut a = a.clone();
674 let b = b.clone();
675 for (k, mut v) in b {
676 if a.contains_key(&k) {
677 a.insert(k.to_string(), merge_deep(a.get(&k).unwrap(), &mut v));
678 } else {
679 a.insert(k.to_string(), v);
680 }
681 }
682 Value::Object(a)
683 }
684 _ => b.clone(),
685 }
686 }
687
688 check_operator_properties(operator, object, |_| false)?;
689 if let Value::Array(items) = _render(value, context)? {
690 let mut new_obj = Value::Object(std::collections::BTreeMap::new());
691 for item in items {
692 if let Value::Object(_) = item {
693 new_obj = merge_deep(&new_obj, &item);
694 } else {
695 return Err(template_error!(
696 "$mergeDeep value must evaluate to an array of objects"
697 ));
698 }
699 }
700 Ok(new_obj)
701 } else {
702 Err(template_error!(
703 "$mergeDeep value must evaluate to an array of objects"
704 ))
705 }
706}
707
708fn reverse_operator(
709 operator: &str,
710 value: &Value,
711 object: &Object,
712 context: &Context,
713) -> Result<Value> {
714 check_operator_properties(operator, object, |_| false)?;
715 if let Value::Array(items) = _render(value, context)? {
716 Ok(Value::Array(items.into_iter().rev().collect()))
717 } else {
718 Err(template_error!("$reverse value must evaluate to an array"))
719 }
720}
721
722fn sort_operator(
723 operator: &str,
724 value: &Value,
725 object: &Object,
726 context: &Context,
727) -> Result<Value> {
728 check_operator_properties(operator, object, |p| parse_by(p).is_some())?;
729
730 let make_err = || {
731 Err(template_error!(
732 "$sorted values to be sorted must have the same type"
733 ))
734 };
735
736 if let Value::Array(arr) = _render(value, context)? {
737 if arr.len() == 0 {
739 return Ok(Value::Array(arr));
740 }
741
742 if object.len() == 1 {
743 return sort_operator_without_by(operator, arr, object, context);
744 }
745
746 let by_props: Vec<_> = object.keys().filter(|k| k != &"$sort").collect();
749 if by_props.len() > 1 {
750 return Err(template_error!("only one by(..) is allowed"));
751 }
752
753 let by_var = parse_by(by_props[0])
754 .ok_or_else(|| template_error!("$sort requires by(identifier) syntax"))?;
755
756 let by_expr = if let Value::String(expr) = object.get(by_props[0]).unwrap() {
757 expr
758 } else {
759 return Err(interpreter_error!("invalid expression in $sorted by"));
760 };
761
762 let mut subcontext = context.child();
763
764 let mut eval_pairs: Vec<(Value, Value)> = arr
770 .iter()
771 .map(|item| {
772 subcontext.insert(by_var, item.clone());
773 Ok((evaluate(by_expr, &subcontext)?, item.clone()))
774 })
775 .collect::<Result<_>>()?;
776
777 if eval_pairs.iter().all(|(e, _v)| e.is_string()) {
778 eval_pairs.sort_by(|a, b| {
780 let a = a.0.as_str().unwrap();
782 let b = b.0.as_str().unwrap();
783 a.cmp(b)
784 });
785 } else if eval_pairs.iter().all(|(e, _v)| e.is_number()) {
786 eval_pairs.sort_by(|a, b| {
788 let a = a.0.as_f64().unwrap();
790 let b = b.0.as_f64().unwrap();
791 a.partial_cmp(b).unwrap()
793 });
794 } else {
795 return make_err();
797 }
798 let result = eval_pairs
799 .into_iter()
800 .map(|(_evaluation, item)| item)
801 .collect();
802 return Ok(Value::Array(result));
803 } else {
804 make_err()
805 }
806}
807
808fn sort_operator_without_by(
809 operator: &str,
810 mut arr: Vec<Value>,
811 object: &Object,
812 context: &Context,
813) -> Result<Value> {
814 let make_err = || {
815 Err(template_error!(
816 "$sorted values to be sorted must have the same type"
817 ))
818 };
819 match arr[0] {
820 Value::String(_) => {
821 for i in &arr {
822 if !i.is_string() {
823 return make_err();
824 }
825 }
826
827 arr.sort_by(|a, b| {
828 let a = a.as_str().unwrap();
830 let b = b.as_str().unwrap();
831 a.cmp(b)
832 });
833 Ok(Value::Array(arr))
834 }
835 Value::Number(_) => {
836 for i in &arr {
837 if !i.is_number() {
838 return make_err();
839 }
840 }
841
842 arr.sort_by(|a, b| {
843 let a = a.as_f64().unwrap();
845 let b = b.as_f64().unwrap();
846 a.partial_cmp(b).unwrap()
848 });
849 Ok(Value::Array(arr))
850 }
851 _ => make_err(),
852 }
853}
854
855pub(crate) fn is_identifier(identifier: &str) -> bool {
857 fn parser(input: &str) -> nom::IResult<&str, &str> {
858 all_consuming(recognize(pair(
859 alt((alpha1, tag("_"))),
860 many0(alt((alphanumeric1, tag("_")))),
861 )))(input)
862 }
863
864 if let Ok((remaining, _)) = parser(identifier) {
865 remaining.is_empty()
866 } else {
867 false
868 }
869}
870
871#[cfg(test)]
872mod tests {
873 use super::is_identifier;
874 use crate::render;
875 use serde_json::json;
876
877 #[test]
878 fn render_returns_correct_template() {
879 let template = json!({"code": 200});
880 let context = json!({});
881 assert_eq!(template, render(&template, &context).unwrap())
882 }
883
884 #[test]
885 fn render_gets_number() {
886 let template = json!(200);
887 let context = json!({});
888 assert_eq!(template, render(&template, &context).unwrap())
889 }
890
891 #[test]
892 fn render_gets_boolean() {
893 let template = json!(true);
894 let context = json!({});
895 assert_eq!(template, render(&template, &context).unwrap())
896 }
897
898 #[test]
899 fn render_gets_null() {
900 let template = json!(null);
901 let context = json!({});
902 assert_eq!(template, render(&template, &context).unwrap())
903 }
904
905 #[test]
906 fn render_gets_string() {
907 let template = "tiny string".into();
908 let context = json!({});
909 assert_eq!(template, render(&template, &context).unwrap())
910 }
911
912 #[test]
913 fn render_gets_array() {
914 let template = json!([1, 2, 3]);
915 let context = json!({});
916 assert_eq!(template, render(&template, &context).unwrap())
917 }
918
919 #[test]
920 fn render_gets_object() {
921 let template = json!({"a":1, "b":2});
922 let context = json!({});
923 assert_eq!(template, render(&template, &context).unwrap())
924 }
925
926 #[test]
927 fn invalid_context() {
928 let template = json!({});
929 assert!(render(&template, &json!(null)).is_err());
930 assert!(render(&template, &json!(false)).is_err());
931 assert!(render(&template, &json!(3.2)).is_err());
932 assert!(render(&template, &json!("two")).is_err());
933 assert!(render(&template, &json!([{}])).is_err());
934 }
935
936 #[test]
937 fn render_array_drops_deletion_markers() {
938 let template = json!([1, {"$if": "false", "then": 1}, 3]);
939 let context = json!({});
940 assert_eq!(render(&template, &context).unwrap(), json!([1, 3]))
941 }
942
943 #[test]
944 fn render_obj_drops_deletion_markers() {
945 let template = json!({"v": {"$if": "false", "then": 1}, "k": "sleutel"});
946 let context = json!({});
947 assert_eq!(
948 render(&template, &context).unwrap(),
949 json!({"k": "sleutel"})
950 )
951 }
952
953 mod check_operator_properties {
954 use super::super::{check_operator_properties, Object};
955 use crate::value::Value;
956
957 fn map(mut keys: Vec<&str>) -> Object {
958 let mut map = Object::new();
959 for key in keys.drain(..) {
960 map.insert(key.into(), Value::Null);
961 }
962 map
963 }
964
965 #[test]
966 fn single_property_is_ok() -> anyhow::Result<()> {
967 check_operator_properties("$foo", &map(vec!["$foo"]), |_| false)
968 }
969
970 #[test]
971 fn allowed_properties_are_ok() -> anyhow::Result<()> {
972 check_operator_properties("$foo", &map(vec!["$foo", "a", "b"]), |k| {
973 k == "a" || k == "b"
974 })
975 }
976
977 #[test]
978 fn missing_allowed_properties_are_ok() -> anyhow::Result<()> {
979 check_operator_properties("$foo", &map(vec!["$foo", "b"]), |k| k == "a" || k == "b")
980 }
981
982 #[test]
983 fn disalloewd_properties_not_ok() {
984 assert_template_error!(
985 check_operator_properties("$foo", &map(vec!["$foo", "nosuch"]), |k| k == "a"),
986 "$foo has undefined properties: nosuch",
987 );
988 }
989
990 #[test]
991 fn disalloewd_properties_sorted() {
992 assert_template_error!(
993 check_operator_properties("$foo", &map(vec!["$foo", "a", "b", "c", "d"]), |k| k
994 == "a"),
995 "$foo has undefined properties: b c d",
996 );
997 }
998 }
999
1000 mod interpolate {
1001 use super::super::interpolate;
1002
1003 use crate::interpreter::Context;
1004 #[test]
1005 fn plain_string() {
1006 let context = Context::new();
1007 assert_eq!(
1008 interpolate("a string", &context).unwrap(),
1009 String::from("a string")
1010 );
1011 }
1012
1013 #[test]
1014 fn interpolation_in_middle() {
1015 let context = Context::new();
1016 assert_eq!(
1017 interpolate("a${13}b", &context).unwrap(),
1018 String::from("a13b")
1019 );
1020 }
1021
1022 #[test]
1023 fn escaped_interpolation() {
1024 let context = Context::new();
1025 assert_eq!(
1026 interpolate("a$${13}b", &context).unwrap(),
1027 String::from("a${13}b")
1028 );
1029 }
1030
1031 #[test]
1032 fn double_escaped_interpolation() {
1033 let context = Context::new();
1034 assert_eq!(
1035 interpolate("a$$${13}b", &context).unwrap(),
1036 String::from("a$${13}b")
1037 );
1038 }
1039
1040 #[test]
1041 fn multibyte_unicode_interpolation_escape() {
1042 let context = Context::new();
1043 assert_eq!(interpolate("a$☃", &context).unwrap(), String::from("a$☃"));
1044 }
1045
1046 #[test]
1047 fn unterminated_interpolation() {
1048 let context = Context::new();
1049 assert!(interpolate("a${13+14", &context).is_err());
1050 }
1051 }
1052
1053 #[test]
1054 fn test_is_identifier() {
1055 assert!(!is_identifier(""));
1056 assert!(!is_identifier("1"));
1057 assert!(!is_identifier("2b"));
1058 assert!(!is_identifier("-"));
1059 assert!(is_identifier("a"));
1060 assert!(is_identifier("abc"));
1061 assert!(is_identifier("abc123"));
1062 assert!(is_identifier("abc_123"));
1063 assert!(!is_identifier("abc-123"));
1064 }
1065}