go_template/
funcs.rs

1//! Builtin functions.
2use std::cmp::Ordering;
3use std::fmt::Write;
4
5use gtmpl_value::{Func, FuncError, Value};
6use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
7
8use crate::printf::sprintf;
9use crate::utils::is_true;
10
11const QUERY_ENCODE: &AsciiSet = &CONTROLS
12    .add(b' ')
13    .add(b'"')
14    .add(b'<')
15    .add(b'>')
16    .add(b'#')
17    .add(b'`')
18    .add(b'`')
19    .add(b'?')
20    .add(b'{')
21    .add(b'}');
22
23pub static BUILTINS: &[(&str, Func)] = &[
24    ("eq", eq as Func),
25    ("ne", ne as Func),
26    ("lt", lt as Func),
27    ("le", le as Func),
28    ("gt", gt as Func),
29    ("ge", ge as Func),
30    ("len", len as Func),
31    ("and", and as Func),
32    ("or", or as Func),
33    ("not", not as Func),
34    ("urlquery", urlquery as Func),
35    ("print", print as Func),
36    ("println", println as Func),
37    ("printf", printf as Func),
38    ("index", index as Func),
39    ("call", call as Func),
40];
41
42macro_rules! val {
43    ($x:expr) => {
44        Value::from($x)
45    };
46}
47
48/// Help to write new functions for gtmpl.
49#[macro_export]
50macro_rules! gtmpl_fn {
51 (
52  $(#[$outer:meta])*
53  fn $name:ident() -> Result<$otyp:ty, FuncError>
54  { $($body:tt)* }
55 ) => {
56  $(#[$outer])*
57  pub fn $name(args: &[$crate::Value]) -> Result<$crate::Value, FuncError> {
58   fn inner() -> Result<$otyp, FuncError> {
59    $($body)*
60   }
61   Ok($crate::Value::from(inner()?))
62  }
63 };
64 (
65  $(#[$outer:meta])*
66  fn $name:ident($arg0:ident : $typ0:ty) -> Result<$otyp:ty, FuncError>
67  { $($body:tt)* }
68 ) => {
69  $(#[$outer])*
70  pub fn $name(
71   args: &[$crate::Value]
72  ) -> Result<$crate::Value, FuncError> {
73   if args.is_empty() {
74    return Err(FuncError::AtLeastXArgs(stringify!($name).into(), 1));
75   }
76   let x = &args[0];
77   let $arg0: $typ0 = $crate::from_value(x)
78    .ok_or(FuncError::UnableToConvertFromValue)?;
79   fn inner($arg0 : $typ0) -> Result<$otyp, FuncError> {
80    $($body)*
81   }
82   let ret: $crate::Value = inner($arg0)?.into();
83   Ok(ret)
84  }
85 };
86 (
87  $(#[$outer:meta])*
88  fn $name:ident($arg0:ident : $typ0:ty$(, $arg:ident : $typ:ty)*) -> Result<$otyp:ty, FuncError>
89  { $($body:tt)* }
90 ) => {
91  $(#[$outer])*
92  pub fn $name(
93   args: &[$crate::Value]
94  ) -> Result<$crate::Value, FuncError> {
95   #[allow(unused_mut)]
96   let mut args = args;
97   if args.is_empty() {
98    return Err(FuncError::AtLeastXArgs(stringify!($name).into(), 1));
99   }
100   let x = &args[0];
101   let $arg0: $typ0 = $crate::from_value(x)
102    .ok_or(FuncError::UnableToConvertFromValue)?;
103   $(args = &args[1..];
104     let x = &args[0];
105     let $arg: $typ = $crate::from_value(x)
106    .ok_or(FuncError::UnableToConvertFromValue)?;)*
107   fn inner($arg0 : $typ0, $($arg : $typ,)*) -> Result<$otyp, FuncError> {
108    $($body)*
109   }
110   let ret: $crate::Value = inner($arg0, $($arg),*)?.into();
111   Ok(ret)
112  }
113 }
114}
115
116macro_rules! gn {
117 (
118  $(#[$outer:meta])*
119  $name:ident($arg1:ident : ref Value, $arg2:ident : ref Value) ->
120   Result<Value, FuncError>
121  { $($body:tt)* }
122 ) => {
123  $(#[$outer])*
124  pub fn $name(args: &[Value]) -> Result<Value, FuncError> {
125   if args.len() != 2 {
126    return Err(FuncError::AtLeastXArgs(stringify!($name).into(), 2));
127   }
128   let $arg1 = &args[0];
129   let $arg2 = &args[1];
130   fn inner($arg1: &Value, $arg2: &Value) -> Result<Value, FuncError> {
131    $($body)*
132   }
133   inner($arg1, $arg2)
134  }
135 }
136}
137
138/// Returns the boolean OR of its arguments by returning the
139/// first non-empty argument or the last argument, that is,
140/// "or x y" behaves as "if x then x else y". All the
141/// arguments are evaluated.
142///
143/// # Example
144/// ```
145/// use gtmpl::template;
146/// let equal = template("{{ or 1 2.0 false . }}", "foo");
147/// assert_eq!(&equal.unwrap(), "1");
148/// ```
149pub fn or(args: &[Value]) -> Result<Value, FuncError> {
150    for arg in args {
151        if is_true(arg) {
152            return Ok(arg.clone());
153        }
154    }
155    args.iter()
156        .cloned()
157        .last()
158        .ok_or_else(|| FuncError::AtLeastXArgs("or".into(), 1))
159}
160
161/// Returns the boolean AND of its arguments by returning the
162/// first empty argument or the last argument, that is,
163/// "and x y" behaves as "if x then y else x". All the
164/// arguments are evaluated.
165///
166/// # Example
167/// ```
168/// use gtmpl::template;
169/// let equal = template("{{ and 1 2.0 true . }}", "foo");
170/// assert_eq!(&equal.unwrap(), "foo");
171/// ```
172pub fn and(args: &[Value]) -> Result<Value, FuncError> {
173    for arg in args {
174        if !is_true(arg) {
175            return Ok(arg.clone());
176        }
177    }
178    args.iter()
179        .cloned()
180        .last()
181        .ok_or_else(|| FuncError::AtLeastXArgs("and".into(), 1))
182}
183
184/// Returns the boolean negation of its single argument.
185///
186/// # Example
187/// ```
188/// use gtmpl::template;
189/// let equal = template("{{ not 0 }}", "");
190/// assert_eq!(&equal.unwrap(), "true");
191/// ```
192pub fn not(args: &[Value]) -> Result<Value, FuncError> {
193    if args.len() != 1 {
194        Err(FuncError::ExactlyXArgs("not".into(), 1))
195    } else {
196        Ok(val!(!is_true(&args[0])))
197    }
198}
199
200/// Returns the integer length of its argument.
201///
202/// # Example
203/// ```
204/// use gtmpl::template;
205/// let equal = template("{{ len . }}", "foo");
206/// assert_eq!(&equal.unwrap(), "3");
207/// ```
208pub fn len(args: &[Value]) -> Result<Value, FuncError> {
209    if args.len() != 1 {
210        return Err(FuncError::ExactlyXArgs("len".into(), 1));
211    }
212    let arg = &args[0];
213    let len = match *arg {
214        Value::String(ref s) => s.len(),
215        Value::Array(ref a) => a.len(),
216        Value::Object(ref o) => o.len(),
217        _ => {
218            return Err(FuncError::Generic(format!("unable to call len on {}", arg)));
219        }
220    };
221
222    Ok(val!(len))
223}
224
225/// Returns the result of calling the first argument, which
226/// must be a function, with the remaining arguments as parameters.
227///
228/// # Example
229/// ```
230/// use gtmpl::{gtmpl_fn, template, Value};
231/// use gtmpl_value::{FuncError, Function};
232///
233/// gtmpl_fn!(
234/// fn add(a: u64, b: u64) -> Result<u64, FuncError> {
235///     Ok(a + b)
236/// });
237/// let equal = template(r#"{{ call . 1 2 }}"#, Value::Function(Function { f: add }));
238/// assert_eq!(&equal.unwrap(), "3");
239/// ```
240pub fn call(args: &[Value]) -> Result<Value, FuncError> {
241    if args.is_empty() {
242        Err(FuncError::AtLeastXArgs("call".into(), 1))
243    } else if let Value::Function(ref f) = args[0] {
244        (f.f)(&args[1..])
245    } else {
246        Err(FuncError::Generic(
247            "call requires the first argument to be a function".into(),
248        ))
249    }
250}
251
252/// An implementation of golang's fmt.Sprint
253///
254/// Golang's Sprint formats using the default formats for its operands and returns the
255/// resulting string. Spaces are added between operands when neither is a string.
256///
257/// # Example
258/// ```
259/// use gtmpl::template;
260/// let equal = template(r#"{{ print "Hello " . "!" }}"#, "world");
261/// assert_eq!(&equal.unwrap(), "Hello world!");
262/// ```
263pub fn print(args: &[Value]) -> Result<Value, FuncError> {
264    let mut no_space = true;
265    let mut s = String::new();
266    for val in args {
267        if let Value::String(ref v) = *val {
268            no_space = true;
269            s.push_str(v);
270        } else {
271            if no_space {
272                s += &val.to_string();
273            } else {
274                s += &format!(" {}", val.to_string())
275            }
276            no_space = false;
277        }
278    }
279    Ok(val!(s))
280}
281
282/// An implementation of golang's fmt.Sprintln
283///
284/// Sprintln formats using the default formats for its operands and returns the
285/// resulting string. Spaces are always added between operands and a newline is appended.
286///
287/// # Example
288/// ```
289/// use gtmpl::template;
290/// let equal = template(r#"{{ println "Hello" . "!" }}"#, "world");
291/// assert_eq!(&equal.unwrap(), "Hello world !\n");
292/// ```
293pub fn println(args: &[Value]) -> Result<Value, FuncError> {
294    let mut iter = args.iter();
295    let s = match iter.next() {
296        None => String::from("\n"),
297        Some(first_elt) => {
298            let (lower, _) = iter.size_hint();
299            let mut result = String::with_capacity(lower + 1);
300            if let Value::String(ref v) = *first_elt {
301                result.push_str(v);
302            } else {
303                write!(&mut result, "{}", first_elt).unwrap();
304            }
305            for elt in iter {
306                result.push(' ');
307                if let Value::String(ref v) = *elt {
308                    result.push_str(v);
309                } else {
310                    write!(&mut result, "{}", elt).unwrap();
311                }
312            }
313            result.push('\n');
314            result
315        }
316    };
317    Ok(val!(s))
318}
319
320/// An implementation of golang's fmt.Sprintf
321/// Limitations:
322/// - float:
323///   * `g`, `G`, and `b` are weired and not implement yet
324/// - pretty sure there are more
325///
326/// # Example
327/// ```
328/// use gtmpl::template;
329/// let equal = template(r#"{{ printf "%v %s %v" "Hello" . "!" }}"#, "world");
330/// assert_eq!(&equal.unwrap(), "Hello world !");
331/// ```
332pub fn printf(args: &[Value]) -> Result<Value, FuncError> {
333    if args.is_empty() {
334        return Err(FuncError::AtLeastXArgs("printf".into(), 1));
335    }
336    if let Value::String(ref s) = args[0] {
337        let s = sprintf(s, &args[1..]).map_err(|e| FuncError::Other(e.into()))?;
338        Ok(val!(s))
339    } else {
340        Err(FuncError::Generic("printf requires a format string".into()))
341    }
342}
343
344/// Returns the result of indexing its first argument by the
345/// following arguments. Thus "index x 1 2 3" is, in Go syntax,
346/// x[1][2][3]. Each indexed item must be a map, slice or array.
347///
348/// # Example
349/// ```
350/// use gtmpl::template;
351/// let ctx = vec![23, 42, 7];
352/// let index = template("{{ index . 1 }}", ctx);
353/// assert_eq!(&index.unwrap(), "42");
354/// ```
355pub fn index(args: &[Value]) -> Result<Value, FuncError> {
356    if args.len() < 2 {
357        return Err(FuncError::AtLeastXArgs("index".into(), 2));
358    }
359    let mut col = &args[0];
360    for val in &args[1..] {
361        col = get_item(col, val)?;
362    }
363
364    Ok(col.clone())
365}
366
367fn get_item<'a>(col: &'a Value, key: &Value) -> Result<&'a Value, FuncError> {
368    let ret = match (col, key) {
369        (&Value::Array(ref a), &Value::Number(ref n)) => {
370            if let Some(i) = n.as_u64() {
371                a.get(i as usize)
372            } else {
373                None
374            }
375        }
376        (&Value::Object(ref o), &Value::Number(ref n))
377        | (&Value::Map(ref o), &Value::Number(ref n)) => o.get(&n.to_string()),
378        (&Value::Object(ref o), &Value::String(ref s))
379        | (&Value::Map(ref o), &Value::String(ref s)) => o.get(s),
380        _ => None,
381    };
382    match *col {
383        Value::Map(_) => Ok(ret.unwrap_or(&Value::NoValue)),
384        _ => ret.ok_or_else(|| FuncError::Generic(format!("unable to get {} in {}", key, col))),
385    }
386}
387
388/// Returns the escaped value of the textual representation of
389/// its arguments in a form suitable for embedding in a URL query.
390///
391/// # Example
392/// ```
393/// use gtmpl::template;
394/// let url = template(r#"{{ urlquery "foo bar?" }}"#, 0);
395/// assert_eq!(&url.unwrap(), "foo%20bar%3F");
396/// ```
397pub fn urlquery(args: &[Value]) -> Result<Value, FuncError> {
398    if args.len() != 1 {
399        return Err(FuncError::ExactlyXArgs("urlquery".into(), 1));
400    }
401    let val = &args[0];
402    match *val {
403        Value::String(ref s) => Ok(val!(utf8_percent_encode(s, QUERY_ENCODE).to_string())),
404        _ => Err(FuncError::Generic(
405            "Arguments need to be of type String".into(),
406        )),
407    }
408}
409
410/// Returns the boolean truth of arg1 == arg2 [== arg3 ...]
411///
412/// # Example
413/// ```
414/// use gtmpl::template;
415/// let equal = template("{{ eq 1 1 . }}", 1);
416/// assert_eq!(&equal.unwrap(), "true");
417/// ```
418pub fn eq(args: &[Value]) -> Result<Value, FuncError> {
419    if args.len() < 2 {
420        return Err(FuncError::AtLeastXArgs("eq".into(), 2));
421    }
422    let first = &args[0];
423    Ok(Value::from(args.iter().skip(1).all(|x| *x == *first)))
424}
425
426gn!(
427#[doc="
428Returns the boolean truth of arg1 != arg2
429
430# Example
431```
432use gtmpl::template;
433let not_equal = template(\"{{ ne 2 . }}\", 1);
434assert_eq!(&not_equal.unwrap(), \"true\");
435```
436"]
437ne(a: ref Value, b: ref Value) -> Result<Value, FuncError> {
438 Ok(Value::from(a != b))
439});
440
441gn!(
442#[doc="
443Returns the boolean truth of arg1 < arg2
444
445# Example
446```
447use gtmpl::template;
448let less_than = template(\"{{ lt 0 . }}\", 1);
449assert_eq!(&less_than.unwrap(), \"true\");
450```
451"]
452lt(a: ref Value, b: ref Value) -> Result<Value, FuncError> {
453 let ret = match cmp(a, b) {
454  None => return Err(FuncError::Generic(format!("unable to compare {} and {}", a, b))),
455  Some(Ordering::Less) => true,
456  _ => false,
457 };
458 Ok(Value::from(ret))
459});
460
461gn!(
462#[doc="
463Returns the boolean truth of arg1 <= arg2
464
465# Example
466```
467use gtmpl::template;
468let less_or_equal = template(\"{{ le 1.4 . }}\", 1.4);
469assert_eq!(less_or_equal.unwrap(), \"true\");
470
471let less_or_equal = template(\"{{ le 0.2 . }}\", 1.4);
472assert_eq!(&less_or_equal.unwrap(), \"true\");
473```
474"]
475le(a: ref Value, b: ref Value) -> Result<Value, FuncError> {
476 let ret = match cmp(a, b) {
477  None => return Err(FuncError::Generic(format!("unable to compare {} and {}", a, b))),
478  Some(Ordering::Less) | Some(Ordering::Equal) => true,
479  _ => false,
480 };
481 Ok(Value::from(ret))
482});
483
484gn!(
485#[doc="
486Returns the boolean truth of arg1 > arg2
487
488# Example
489```
490use gtmpl::template;
491let greater_than = template(\"{{ gt 1.4 . }}\", 1.2);
492assert_eq!(&greater_than.unwrap(), \"true\");
493```
494"]
495gt(a: ref Value, b: ref Value) -> Result<Value, FuncError> {
496 let ret = match cmp(a, b) {
497  None => return Err(FuncError::Generic(format!("unable to compare {} and {}", a, b))),
498  Some(Ordering::Greater) => true,
499  _ => false,
500 };
501 Ok(Value::from(ret))
502});
503
504gn!(
505#[doc="
506Returns the boolean truth of arg1 >= arg2
507
508# Example
509```
510use gtmpl::template;
511let greater_or_equal = template(\"{{ ge 1.4 1.3 }}\", 1.2);
512assert_eq!(greater_or_equal.unwrap(), \"true\");
513
514let greater_or_equal = template(\"{{ ge 1.4 . }}\", 0.2);
515assert_eq!(&greater_or_equal.unwrap(), \"true\");
516```
517"]
518ge(a: ref Value, b: ref Value) -> Result<Value, FuncError> {
519 let ret = match cmp(a, b) {
520  None => return Err(FuncError::Generic(format!("unable to compare {} and {}", a, b))),
521  Some(Ordering::Greater) | Some(Ordering::Equal) => true,
522  _ => false,
523 };
524 Ok(Value::from(ret))
525});
526
527fn cmp(left: &Value, right: &Value) -> Option<Ordering> {
528    match (left, right) {
529        (&Value::Number(ref l), &Value::Number(ref r)) => {
530            if let (Some(lf), Some(rf)) = (l.as_f64(), r.as_f64()) {
531                return lf.partial_cmp(&rf);
532            }
533            if let (Some(li), Some(ri)) = (l.as_i64(), r.as_i64()) {
534                return li.partial_cmp(&ri);
535            }
536            if let (Some(lu), Some(ru)) = (l.as_u64(), r.as_u64()) {
537                return lu.partial_cmp(&ru);
538            }
539            None
540        }
541        (&Value::Bool(ref l), &Value::Bool(ref r)) => l.partial_cmp(r),
542        (&Value::String(ref l), &Value::String(ref r)) => l.partial_cmp(r),
543        (&Value::Array(ref l), &Value::Array(ref r)) => l.len().partial_cmp(&r.len()),
544        _ => None,
545    }
546}
547
548#[cfg(test)]
549mod tests_mocked {
550    use super::*;
551    use std::collections::HashMap;
552
553    #[test]
554    fn test_macro() {
555        gtmpl_fn!(
556            fn f1(i: i64) -> Result<i64, FuncError> {
557                Ok(i + 1)
558            }
559        );
560        let vals: Vec<Value> = vec![val!(1i64)];
561        let ret = f1(&vals);
562        assert_eq!(ret.unwrap(), Value::from(2i64));
563
564        gtmpl_fn!(
565            fn f3(i: i64, j: i64, k: i64) -> Result<i64, FuncError> {
566                Ok(i + j + k)
567            }
568        );
569        let vals: Vec<Value> = vec![val!(1i64), val!(2i64), val!(3i64)];
570        let ret = f3(&vals);
571        assert_eq!(ret.unwrap(), Value::from(6i64));
572    }
573
574    #[test]
575    fn test_eq() {
576        let vals: Vec<Value> = vec![val!("foo".to_owned()), val!("foo".to_owned())];
577        let ret = eq(&vals);
578        assert_eq!(ret.unwrap(), Value::Bool(true));
579        let vals: Vec<Value> = vec![val!(1u32), val!(1u32), val!(1i8)];
580        let ret = eq(&vals);
581        assert_eq!(ret.unwrap(), Value::Bool(true));
582        let vals: Vec<Value> = vec![val!(false), val!(false), val!(false)];
583        let ret = eq(&vals);
584        assert_eq!(ret.unwrap(), Value::Bool(true));
585    }
586
587    #[test]
588    fn test_and() {
589        let vals: Vec<Value> = vec![val!(0i32), val!(1u8)];
590        let ret = and(&vals);
591        assert_eq!(ret.unwrap(), Value::from(0i32));
592
593        let vals: Vec<Value> = vec![val!(1i32), val!(2u8)];
594        let ret = and(&vals);
595        assert_eq!(ret.unwrap(), Value::from(2u8));
596    }
597
598    #[test]
599    fn test_or() {
600        let vals: Vec<Value> = vec![val!(0i32), val!(1u8)];
601        let ret = or(&vals);
602        assert_eq!(ret.unwrap(), Value::from(1u8));
603
604        let vals: Vec<Value> = vec![val!(0i32), val!(0u8)];
605        let ret = or(&vals);
606        assert_eq!(ret.unwrap(), Value::from(0u8));
607    }
608
609    #[test]
610    fn test_ne() {
611        let vals: Vec<Value> = vec![val!(0i32), val!(1u8)];
612        let ret = ne(&vals);
613        assert_eq!(ret.unwrap(), Value::from(true));
614
615        let vals: Vec<Value> = vec![val!(0i32), val!(0u8)];
616        let ret = ne(&vals);
617        assert_eq!(ret.unwrap(), Value::from(false));
618
619        let vals: Vec<Value> = vec![val!("foo"), val!("bar")];
620        let ret = ne(&vals);
621        assert_eq!(ret.unwrap(), Value::from(true));
622
623        let vals: Vec<Value> = vec![val!("foo"), val!("foo")];
624        let ret = ne(&vals);
625        assert_eq!(ret.unwrap(), Value::from(false));
626    }
627
628    #[test]
629    fn test_lt() {
630        let vals: Vec<Value> = vec![val!(-1i32), val!(1u8)];
631        let ret = lt(&vals);
632        assert_eq!(ret.unwrap(), Value::from(true));
633
634        let vals: Vec<Value> = vec![val!(0i32), val!(0u8)];
635        let ret = lt(&vals);
636        assert_eq!(ret.unwrap(), Value::from(false));
637
638        let vals: Vec<Value> = vec![val!(1i32), val!(0u8)];
639        let ret = lt(&vals);
640        assert_eq!(ret.unwrap(), Value::from(false));
641    }
642
643    #[test]
644    fn test_le() {
645        let vals: Vec<Value> = vec![val!(-1i32), val!(1u8)];
646        let ret = le(&vals);
647        assert_eq!(ret.unwrap(), Value::from(true));
648
649        let vals: Vec<Value> = vec![val!(0i32), val!(0u8)];
650        let ret = le(&vals);
651        assert_eq!(ret.unwrap(), Value::from(true));
652
653        let vals: Vec<Value> = vec![val!(1i32), val!(0u8)];
654        let ret = le(&vals);
655        assert_eq!(ret.unwrap(), Value::from(false));
656    }
657
658    #[test]
659    fn test_gt() {
660        let vals: Vec<Value> = vec![val!(-1i32), val!(1u8)];
661        let ret = gt(&vals);
662        assert_eq!(ret.unwrap(), Value::from(false));
663
664        let vals: Vec<Value> = vec![val!(0i32), val!(0u8)];
665        let ret = gt(&vals);
666        assert_eq!(ret.unwrap(), Value::from(false));
667
668        let vals: Vec<Value> = vec![val!(1i32), val!(0u8)];
669        let ret = gt(&vals);
670        assert_eq!(ret.unwrap(), Value::from(true));
671    }
672
673    #[test]
674    fn test_ge() {
675        let vals: Vec<Value> = vec![val!(-1i32), val!(1u8)];
676        let ret = ge(&vals);
677        assert_eq!(ret.unwrap(), Value::from(false));
678
679        let vals: Vec<Value> = vec![val!(0i32), val!(0u8)];
680        let ret = ge(&vals);
681        assert_eq!(ret.unwrap(), Value::from(true));
682
683        let vals: Vec<Value> = vec![val!(1i32), val!(0u8)];
684        let ret = ge(&vals);
685        assert_eq!(ret.unwrap(), Value::from(true));
686    }
687
688    #[test]
689    fn test_print() {
690        let vals: Vec<Value> = vec![val!("foo"), val!(1u8)];
691        let ret = print(&vals);
692        assert_eq!(ret.unwrap(), Value::from("foo1"));
693
694        let vals: Vec<Value> = vec![val!("foo"), val!(1u8), val!(2)];
695        let ret = print(&vals);
696        assert_eq!(ret.unwrap(), Value::from("foo1 2"));
697
698        let vals: Vec<Value> = vec![val!(true), val!(1), val!("foo"), val!(2)];
699        let ret = print(&vals);
700        assert_eq!(ret.unwrap(), Value::from("true 1foo2"));
701    }
702
703    #[test]
704    fn test_println() {
705        let vals: Vec<Value> = vec![val!("foo"), val!(1u8)];
706        let ret = println(&vals);
707        assert_eq!(ret.unwrap(), Value::from("foo 1\n"));
708
709        let vals: Vec<Value> = vec![];
710        let ret = println(&vals);
711        assert_eq!(ret.unwrap(), Value::from("\n"));
712    }
713
714    #[test]
715    fn test_index() {
716        let vals: Vec<Value> = vec![val!(vec![vec![1, 2], vec![3, 4]]), val!(1), val!(0)];
717        let ret = index(&vals);
718        assert_eq!(ret.unwrap(), Value::from(3));
719
720        let mut o = HashMap::new();
721        o.insert(String::from("foo"), vec![String::from("bar")]);
722        let col = Value::from(o);
723        let vals: Vec<Value> = vec![col, val!("foo"), val!(0)];
724        let ret = index(&vals);
725        assert_eq!(ret.unwrap(), Value::from("bar"));
726
727        let mut o = HashMap::new();
728        o.insert(String::from("foo"), String::from("bar"));
729        let col = Value::from(o);
730        let vals: Vec<Value> = vec![col, val!("foo2")];
731        let ret = index(&vals);
732        assert_eq!(ret.unwrap(), Value::NoValue);
733    }
734
735    #[test]
736    fn test_builtins() {
737        let vals: Vec<Value> = vec![val!("foo".to_owned()), val!("foo".to_owned())];
738        let builtin_eq = BUILTINS
739            .iter()
740            .find(|&&(n, _)| n == "eq")
741            .map(|&(_, f)| f)
742            .unwrap();
743        let ret = builtin_eq(&vals);
744        assert_eq!(ret.unwrap(), Value::Bool(true));
745    }
746
747    #[test]
748    fn test_gtmpl_fn() {
749        gtmpl_fn!(
750            fn add(a: u64, b: u64) -> Result<u64, FuncError> {
751                Ok(a + b)
752            }
753        );
754        let vals: Vec<Value> = vec![val!(1u32), val!(2u32)];
755        let ret = add(&vals);
756        assert_eq!(ret.unwrap(), Value::from(3u32));
757
758        gtmpl_fn!(
759            fn has_prefix(s: String, prefix: String) -> Result<bool, FuncError> {
760                Ok(s.starts_with(&prefix))
761            }
762        );
763        let vals: Vec<Value> = vec![val!("foobar"), val!("foo")];
764        let ret = has_prefix(&vals);
765        assert_eq!(ret.unwrap(), Value::from(true));
766    }
767}