use crate::Value;
use crate::args::Kwargs;
use crate::errors::{Error, TeraResult};
use crate::value::FunctionResult;
use crate::vm::state::State;
use std::sync::Arc;
pub trait Function<Res>: Sync + Send + 'static {
fn call(&self, kwargs: Kwargs, state: &State) -> Res;
fn is_safe(&self) -> bool {
false
}
}
impl<Func, Res> Function<Res> for Func
where
Func: Fn(Kwargs, &State) -> Res + Sync + Send + 'static,
Res: FunctionResult,
{
fn call(&self, kwargs: Kwargs, state: &State) -> Res {
(self)(kwargs, state)
}
}
type FunctionFunc = dyn Fn(Kwargs, &State) -> TeraResult<Value> + Sync + Send + 'static;
#[derive(Clone)]
pub(crate) struct StoredFunction {
func: Arc<FunctionFunc>,
is_safe: bool,
}
impl StoredFunction {
pub fn new<Func, Res>(f: Func) -> Self
where
Func: Function<Res>,
Res: FunctionResult,
{
let is_safe = f.is_safe();
let closure = move |kwargs, state: &State| -> TeraResult<Value> {
f.call(kwargs, state).into_result()
};
StoredFunction {
func: Arc::new(closure),
is_safe,
}
}
pub fn call(&self, kwargs: Kwargs, state: &State) -> TeraResult<Value> {
(self.func)(kwargs, state)
}
pub fn is_safe(&self) -> bool {
self.is_safe
}
}
const MAX_RANGE_LEN: usize = 100_000;
pub(crate) fn range(kwargs: Kwargs, _: &State) -> TeraResult<Vec<isize>> {
let start = kwargs.get::<isize>("start")?.unwrap_or_default();
let end = kwargs.must_get::<isize>("end")?;
let step_by = kwargs.get::<usize>("step_by")?.unwrap_or(1);
if start > end {
return Err(Error::message(
"Function `range` was called with a `start` argument greater than the `end` one",
));
}
if step_by == 0 {
return Err(Error::message(
"Function `range` was called with a `step_by` argument of 0",
));
}
let span = (end as i128) - (start as i128);
let step = step_by as i128;
let len = (span + step - 1) / step;
if len > MAX_RANGE_LEN as i128 {
return Err(Error::message(format!(
"Function `range` would produce {len} elements, which exceeds the limit of {MAX_RANGE_LEN}"
)));
}
Ok((start..end).step_by(step_by).collect())
}
pub(crate) fn throw(kwargs: Kwargs, _: &State) -> TeraResult<bool> {
let message = kwargs.must_get::<&str>("message")?;
Err(Error::message(message))
}