1 use std
::collections
::HashMap
;
3 #[cfg(feature = "builtins")]
4 use chrono
::prelude
::*;
5 #[cfg(feature = "builtins")]
7 use serde_json
::value
::{from_value, to_value, Value}
;
9 use crate::errors
::{Error, Result}
;
11 /// The global function type definition
12 pub trait Function
: Sync
+ Send
{
13 /// The global function type definition
14 fn call(&self, args
: &HashMap
<String
, Value
>) -> Result
<Value
>;
16 /// Whether the current function's output should be treated as safe, defaults to `false`
17 fn is_safe(&self) -> bool
{
22 impl<F
> Function
for F
24 F
: Fn(&HashMap
<String
, Value
>) -> Result
<Value
> + Sync
+ Send
,
26 fn call(&self, args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
31 pub fn range(args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
32 let start
= match args
.get("start") {
33 Some(val
) => match from_value
::<usize>(val
.clone()) {
36 return Err(Error
::msg(format
!(
37 "Function `range` received start={} but `start` can only be a number",
44 let step_by
= match args
.get("step_by") {
45 Some(val
) => match from_value
::<usize>(val
.clone()) {
48 return Err(Error
::msg(format
!(
49 "Function `range` received step_by={} but `step` can only be a number",
56 let end
= match args
.get("end") {
57 Some(val
) => match from_value
::<usize>(val
.clone()) {
60 return Err(Error
::msg(format
!(
61 "Function `range` received end={} but `end` can only be a number",
67 return Err(Error
::msg("Function `range` was called without a `end` argument"));
72 return Err(Error
::msg(
73 "Function `range` was called with a `start` argument greater than the `end` one",
83 Ok(to_value(res
).unwrap())
86 #[cfg(feature = "builtins")]
87 pub fn now(args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
88 let utc
= match args
.get("utc") {
89 Some(val
) => match from_value
::<bool
>(val
.clone()) {
92 return Err(Error
::msg(format
!(
93 "Function `now` received utc={} but `utc` can only be a boolean",
100 let timestamp
= match args
.get("timestamp") {
101 Some(val
) => match from_value
::<bool
>(val
.clone()) {
104 return Err(Error
::msg(format
!(
105 "Function `now` received timestamp={} but `timestamp` can only be a boolean",
114 let datetime
= Utc
::now();
116 return Ok(to_value(datetime
.timestamp()).unwrap());
118 Ok(to_value(datetime
.to_rfc3339()).unwrap())
120 let datetime
= Local
::now();
122 return Ok(to_value(datetime
.timestamp()).unwrap());
124 Ok(to_value(datetime
.to_rfc3339()).unwrap())
128 pub fn throw(args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
129 match args
.get("message") {
130 Some(val
) => match from_value
::<String
>(val
.clone()) {
131 Ok(v
) => Err(Error
::msg(v
)),
132 Err(_
) => Err(Error
::msg(format
!(
133 "Function `throw` received message={} but `message` can only be a string",
137 None
=> Err(Error
::msg("Function `throw` was called without a `message` argument")),
141 #[cfg(feature = "builtins")]
142 pub fn get_random(args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
143 let start
= match args
.get("start") {
144 Some(val
) => match from_value
::<i32>(val
.clone()) {
147 return Err(Error
::msg(format
!(
148 "Function `get_random` received start={} but `start` can only be a boolean",
156 let end
= match args
.get("end") {
157 Some(val
) => match from_value
::<i32>(val
.clone()) {
160 return Err(Error
::msg(format
!(
161 "Function `get_random` received end={} but `end` can only be a boolean",
166 None
=> return Err(Error
::msg("Function `get_random` didn't receive an `end` argument")),
168 let mut rng
= rand
::thread_rng();
169 let res
= rng
.gen_range(start
..end
);
171 Ok(Value
::Number(res
.into()))
174 pub fn get_env(args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
175 let name
= match args
.get("name") {
176 Some(val
) => match from_value
::<String
>(val
.clone()) {
179 return Err(Error
::msg(format
!(
180 "Function `get_env` received name={} but `name` can only be a string",
185 None
=> return Err(Error
::msg("Function `get_env` didn't receive a `name` argument")),
188 match std
::env
::var(&name
).ok() {
189 Some(res
) => Ok(Value
::String(res
)),
190 None
=> match args
.get("default") {
191 Some(default) => Ok(default.clone()),
192 None
=> Err(Error
::msg(format
!("Environment variable `{}` not found", &name
))),
199 use std
::collections
::HashMap
;
201 use serde_json
::value
::to_value
;
207 let mut args
= HashMap
::new();
208 args
.insert("end".to_string(), to_value(5).unwrap());
210 let res
= range(&args
).unwrap();
211 assert_eq
!(res
, to_value(vec
![0, 1, 2, 3, 4]).unwrap());
216 let mut args
= HashMap
::new();
217 args
.insert("end".to_string(), to_value(5).unwrap());
218 args
.insert("start".to_string(), to_value(1).unwrap());
220 let res
= range(&args
).unwrap();
221 assert_eq
!(res
, to_value(vec
![1, 2, 3, 4]).unwrap());
225 fn range_start_greater_than_end() {
226 let mut args
= HashMap
::new();
227 args
.insert("end".to_string(), to_value(5).unwrap());
228 args
.insert("start".to_string(), to_value(6).unwrap());
230 assert
!(range(&args
).is_err());
235 let mut args
= HashMap
::new();
236 args
.insert("end".to_string(), to_value(10).unwrap());
237 args
.insert("step_by".to_string(), to_value(2).unwrap());
239 let res
= range(&args
).unwrap();
240 assert_eq
!(res
, to_value(vec
![0, 2, 4, 6, 8]).unwrap());
243 #[cfg(feature = "builtins")]
246 let args
= HashMap
::new();
248 let res
= now(&args
).unwrap();
249 assert
!(res
.is_string());
250 assert
!(res
.as_str().unwrap().contains("T"));
253 #[cfg(feature = "builtins")]
255 fn now_datetime_utc() {
256 let mut args
= HashMap
::new();
257 args
.insert("utc".to_string(), to_value(true).unwrap());
259 let res
= now(&args
).unwrap();
260 assert
!(res
.is_string());
261 let val
= res
.as_str().unwrap();
263 assert
!(val
.contains("T"));
264 assert
!(val
.contains("+00:00"));
267 #[cfg(feature = "builtins")]
270 let mut args
= HashMap
::new();
271 args
.insert("timestamp".to_string(), to_value(true).unwrap());
273 let res
= now(&args
).unwrap();
274 assert
!(res
.is_number());
278 fn throw_errors_with_message() {
279 let mut args
= HashMap
::new();
280 args
.insert("message".to_string(), to_value("Hello").unwrap());
282 let res
= throw(&args
);
283 assert
!(res
.is_err());
284 let err
= res
.unwrap_err();
285 assert_eq
!(err
.to_string(), "Hello");
288 #[cfg(feature = "builtins")]
290 fn get_random_no_start() {
291 let mut args
= HashMap
::new();
292 args
.insert("end".to_string(), to_value(10).unwrap());
293 let res
= get_random(&args
).unwrap();
295 assert
!(res
.is_number());
296 assert
!(res
.as_i64().unwrap() >= 0);
297 assert
!(res
.as_i64().unwrap() < 10);
300 #[cfg(feature = "builtins")]
302 fn get_random_with_start() {
303 let mut args
= HashMap
::new();
304 args
.insert("start".to_string(), to_value(5).unwrap());
305 args
.insert("end".to_string(), to_value(10).unwrap());
306 let res
= get_random(&args
).unwrap();
308 assert
!(res
.is_number());
309 assert
!(res
.as_i64().unwrap() >= 5);
310 assert
!(res
.as_i64().unwrap() < 10);
314 fn get_env_existing() {
315 std
::env
::set_var("TERA_TEST", "true");
316 let mut args
= HashMap
::new();
317 args
.insert("name".to_string(), to_value("TERA_TEST").unwrap());
318 let res
= get_env(&args
).unwrap();
319 assert
!(res
.is_string());
320 assert_eq
!(res
.as_str().unwrap(), "true");
321 std
::env
::remove_var("TERA_TEST");
325 fn get_env_non_existing_no_default() {
326 let mut args
= HashMap
::new();
327 args
.insert("name".to_string(), to_value("UNKNOWN_VAR").unwrap());
328 let res
= get_env(&args
);
329 assert
!(res
.is_err());
333 fn get_env_non_existing_with_default() {
334 let mut args
= HashMap
::new();
335 args
.insert("name".to_string(), to_value("UNKNOWN_VAR").unwrap());
336 args
.insert("default".to_string(), to_value("false").unwrap());
337 let res
= get_env(&args
).unwrap();
338 assert
!(res
.is_string());
339 assert_eq
!(res
.as_str().unwrap(), "false");