]> git.proxmox.com Git - rustc.git/blob - vendor/tera/src/builtins/functions.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / vendor / tera / src / builtins / functions.rs
1 use std::collections::HashMap;
2
3 #[cfg(feature = "builtins")]
4 use chrono::prelude::*;
5 #[cfg(feature = "builtins")]
6 use rand::Rng;
7 use serde_json::value::{from_value, to_value, Value};
8
9 use crate::errors::{Error, Result};
10
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>;
15
16 /// Whether the current function's output should be treated as safe, defaults to `false`
17 fn is_safe(&self) -> bool {
18 false
19 }
20 }
21
22 impl<F> Function for F
23 where
24 F: Fn(&HashMap<String, Value>) -> Result<Value> + Sync + Send,
25 {
26 fn call(&self, args: &HashMap<String, Value>) -> Result<Value> {
27 self(args)
28 }
29 }
30
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()) {
34 Ok(v) => v,
35 Err(_) => {
36 return Err(Error::msg(format!(
37 "Function `range` received start={} but `start` can only be a number",
38 val
39 )));
40 }
41 },
42 None => 0,
43 };
44 let step_by = match args.get("step_by") {
45 Some(val) => match from_value::<usize>(val.clone()) {
46 Ok(v) => v,
47 Err(_) => {
48 return Err(Error::msg(format!(
49 "Function `range` received step_by={} but `step` can only be a number",
50 val
51 )));
52 }
53 },
54 None => 1,
55 };
56 let end = match args.get("end") {
57 Some(val) => match from_value::<usize>(val.clone()) {
58 Ok(v) => v,
59 Err(_) => {
60 return Err(Error::msg(format!(
61 "Function `range` received end={} but `end` can only be a number",
62 val
63 )));
64 }
65 },
66 None => {
67 return Err(Error::msg("Function `range` was called without a `end` argument"));
68 }
69 };
70
71 if start > end {
72 return Err(Error::msg(
73 "Function `range` was called with a `start` argument greater than the `end` one",
74 ));
75 }
76
77 let mut i = start;
78 let mut res = vec![];
79 while i < end {
80 res.push(i);
81 i += step_by;
82 }
83 Ok(to_value(res).unwrap())
84 }
85
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()) {
90 Ok(v) => v,
91 Err(_) => {
92 return Err(Error::msg(format!(
93 "Function `now` received utc={} but `utc` can only be a boolean",
94 val
95 )));
96 }
97 },
98 None => false,
99 };
100 let timestamp = match args.get("timestamp") {
101 Some(val) => match from_value::<bool>(val.clone()) {
102 Ok(v) => v,
103 Err(_) => {
104 return Err(Error::msg(format!(
105 "Function `now` received timestamp={} but `timestamp` can only be a boolean",
106 val
107 )));
108 }
109 },
110 None => false,
111 };
112
113 if utc {
114 let datetime = Utc::now();
115 if timestamp {
116 return Ok(to_value(datetime.timestamp()).unwrap());
117 }
118 Ok(to_value(datetime.to_rfc3339()).unwrap())
119 } else {
120 let datetime = Local::now();
121 if timestamp {
122 return Ok(to_value(datetime.timestamp()).unwrap());
123 }
124 Ok(to_value(datetime.to_rfc3339()).unwrap())
125 }
126 }
127
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",
134 val
135 ))),
136 },
137 None => Err(Error::msg("Function `throw` was called without a `message` argument")),
138 }
139 }
140
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()) {
145 Ok(v) => v,
146 Err(_) => {
147 return Err(Error::msg(format!(
148 "Function `get_random` received start={} but `start` can only be a boolean",
149 val
150 )));
151 }
152 },
153 None => 0,
154 };
155
156 let end = match args.get("end") {
157 Some(val) => match from_value::<i32>(val.clone()) {
158 Ok(v) => v,
159 Err(_) => {
160 return Err(Error::msg(format!(
161 "Function `get_random` received end={} but `end` can only be a boolean",
162 val
163 )));
164 }
165 },
166 None => return Err(Error::msg("Function `get_random` didn't receive an `end` argument")),
167 };
168 let mut rng = rand::thread_rng();
169 let res = rng.gen_range(start..end);
170
171 Ok(Value::Number(res.into()))
172 }
173
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()) {
177 Ok(v) => v,
178 Err(_) => {
179 return Err(Error::msg(format!(
180 "Function `get_env` received name={} but `name` can only be a string",
181 val
182 )));
183 }
184 },
185 None => return Err(Error::msg("Function `get_env` didn't receive a `name` argument")),
186 };
187
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))),
193 },
194 }
195 }
196
197 #[cfg(test)]
198 mod tests {
199 use std::collections::HashMap;
200
201 use serde_json::value::to_value;
202
203 use super::*;
204
205 #[test]
206 fn range_default() {
207 let mut args = HashMap::new();
208 args.insert("end".to_string(), to_value(5).unwrap());
209
210 let res = range(&args).unwrap();
211 assert_eq!(res, to_value(vec![0, 1, 2, 3, 4]).unwrap());
212 }
213
214 #[test]
215 fn range_start() {
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());
219
220 let res = range(&args).unwrap();
221 assert_eq!(res, to_value(vec![1, 2, 3, 4]).unwrap());
222 }
223
224 #[test]
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());
229
230 assert!(range(&args).is_err());
231 }
232
233 #[test]
234 fn range_step_by() {
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());
238
239 let res = range(&args).unwrap();
240 assert_eq!(res, to_value(vec![0, 2, 4, 6, 8]).unwrap());
241 }
242
243 #[cfg(feature = "builtins")]
244 #[test]
245 fn now_default() {
246 let args = HashMap::new();
247
248 let res = now(&args).unwrap();
249 assert!(res.is_string());
250 assert!(res.as_str().unwrap().contains("T"));
251 }
252
253 #[cfg(feature = "builtins")]
254 #[test]
255 fn now_datetime_utc() {
256 let mut args = HashMap::new();
257 args.insert("utc".to_string(), to_value(true).unwrap());
258
259 let res = now(&args).unwrap();
260 assert!(res.is_string());
261 let val = res.as_str().unwrap();
262 println!("{}", val);
263 assert!(val.contains("T"));
264 assert!(val.contains("+00:00"));
265 }
266
267 #[cfg(feature = "builtins")]
268 #[test]
269 fn now_timestamp() {
270 let mut args = HashMap::new();
271 args.insert("timestamp".to_string(), to_value(true).unwrap());
272
273 let res = now(&args).unwrap();
274 assert!(res.is_number());
275 }
276
277 #[test]
278 fn throw_errors_with_message() {
279 let mut args = HashMap::new();
280 args.insert("message".to_string(), to_value("Hello").unwrap());
281
282 let res = throw(&args);
283 assert!(res.is_err());
284 let err = res.unwrap_err();
285 assert_eq!(err.to_string(), "Hello");
286 }
287
288 #[cfg(feature = "builtins")]
289 #[test]
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();
294 println!("{}", res);
295 assert!(res.is_number());
296 assert!(res.as_i64().unwrap() >= 0);
297 assert!(res.as_i64().unwrap() < 10);
298 }
299
300 #[cfg(feature = "builtins")]
301 #[test]
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();
307 println!("{}", res);
308 assert!(res.is_number());
309 assert!(res.as_i64().unwrap() >= 5);
310 assert!(res.as_i64().unwrap() < 10);
311 }
312
313 #[test]
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");
322 }
323
324 #[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());
330 }
331
332 #[test]
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");
340 }
341 }