Skip to main content

tera/
functions.rs

1use crate::Value;
2use crate::args::Kwargs;
3use crate::errors::{Error, TeraResult};
4use crate::value::FunctionResult;
5use crate::vm::state::State;
6use std::sync::Arc;
7
8/// The function function type definition
9pub trait Function<Res: FunctionResult>: Sync + Send + 'static {
10    /// The function type definition
11    fn call(&self, kwargs: Kwargs, state: &State) -> Res;
12
13    /// Whether the current function's output should be treated as safe, defaults to `false`
14    /// Only needs to be defined if the filter returns a string
15    fn is_safe(&self) -> bool {
16        false
17    }
18}
19
20impl<Func, Res> Function<Res> for Func
21where
22    Func: Fn(Kwargs, &State) -> Res + Sync + Send + 'static,
23    Res: FunctionResult,
24{
25    fn call(&self, kwargs: Kwargs, state: &State) -> Res {
26        (self)(kwargs, state)
27    }
28}
29
30type FunctionFunc = dyn Fn(Kwargs, &State) -> TeraResult<Value> + Sync + Send + 'static;
31
32#[derive(Clone)]
33pub(crate) struct StoredFunction {
34    func: Arc<FunctionFunc>,
35    is_safe: bool,
36}
37
38impl StoredFunction {
39    pub fn new<Func, Res>(f: Func) -> Self
40    where
41        Func: Function<Res>,
42        Res: FunctionResult,
43    {
44        let is_safe = f.is_safe();
45        let closure = move |kwargs, state: &State| -> TeraResult<Value> {
46            f.call(kwargs, state).into_result()
47        };
48
49        StoredFunction {
50            func: Arc::new(closure),
51            is_safe,
52        }
53    }
54
55    pub fn call(&self, kwargs: Kwargs, state: &State) -> TeraResult<Value> {
56        (self.func)(kwargs, state)
57    }
58
59    pub fn is_safe(&self) -> bool {
60        self.is_safe
61    }
62}
63
64/// Upper bound on the number of elements `range()` will produce to avoid OOM.
65const MAX_RANGE_LEN: usize = 100_000;
66
67pub(crate) fn range(kwargs: Kwargs, _: &State) -> TeraResult<Vec<i128>> {
68    let start = kwargs.get::<i128>("start")?.unwrap_or_default();
69    let end = kwargs.must_get::<i128>("end")?;
70    let step_by = kwargs.get::<i128>("step_by")?.unwrap_or(1);
71    if start > end && step_by > 0 {
72        return Err(Error::message(
73            "Function `range` was called with a `start` argument greater than the `end` one",
74        ));
75    }
76    if step_by == 0 {
77        return Err(Error::message(
78            "Function `range` was called with a `step_by` argument of 0",
79        ));
80    }
81
82    let overflow =
83        || Error::message("Function `range` was called with arguments that overflow i128");
84    let len = if step_by > 0 {
85        let span = end.checked_sub(start).ok_or_else(overflow)?;
86        span.checked_add(step_by - 1).ok_or_else(overflow)? / step_by
87    } else if start <= end {
88        0
89    } else {
90        let step = step_by.checked_neg().ok_or_else(overflow)?;
91        let span = start.checked_sub(end).ok_or_else(overflow)?;
92        span.checked_add(step - 1).ok_or_else(overflow)? / step
93    };
94    if len > MAX_RANGE_LEN as i128 {
95        return Err(Error::message(format!(
96            "Function `range` would produce {len} elements, which exceeds the limit of {MAX_RANGE_LEN}"
97        )));
98    }
99
100    let mut values = Vec::with_capacity(len as usize);
101    for i in 0..len {
102        values.push(start + i * step_by);
103    }
104    Ok(values)
105}
106
107pub(crate) fn throw(kwargs: Kwargs, _: &State) -> TeraResult<bool> {
108    let message = kwargs.must_get::<&str>("message")?;
109    Err(Error::message(message))
110}