tera_rand/
primitives.rs

1use crate::common::parse_range_and_gen_value_in_range;
2use rand::random;
3use std::collections::HashMap;
4use tera::{to_value, Result, Value};
5
6/// A Tera function to generate a random boolean.
7///
8/// # Example usage
9///
10/// ```edition2021
11/// use tera::{Context, Tera};
12/// use tera_rand::random_bool;
13///
14/// let mut tera: Tera = Tera::default();
15/// tera.register_function("random_bool", random_bool);
16///
17/// let context: Context = Context::new();
18/// let rendered: String = tera.render_str("{{ random_bool() }}", &context).unwrap();
19/// ```
20pub fn random_bool(_args: &HashMap<String, Value>) -> tera::Result<Value> {
21    let random_value: bool = random::<bool>();
22    let json_value: Value = to_value(random_value)?;
23    Ok(json_value)
24}
25
26/// A Tera function to generate a random char.
27///
28/// # Example usage
29///
30/// ```edition2021
31/// use tera::{Context, Tera};
32/// use tera_rand::random_char;
33///
34/// let mut tera: Tera = Tera::default();
35/// tera.register_function("random_char", random_char);
36///
37/// let context: Context = Context::new();
38/// let rendered: String = tera.render_str("{{ random_char() }}", &context).unwrap();
39/// ```
40pub fn random_char(_args: &HashMap<String, Value>) -> Result<Value> {
41    let random_value: char = random::<char>();
42    let json_value: Value = to_value(random_value)?;
43    Ok(json_value)
44}
45
46/// A Tera function to generate a random unsigned 32-bit integer.
47///
48/// The `start` parameter takes an unsigned 32-bit integer to indicate the beginning of the
49/// range (inclusive). If `start` is not passed in, it defaults to `u32::MIN`.
50///
51/// The `end` parameter also takes an unsigned 32-bit integer indicating the end of the range,
52/// which is also inclusive. An inclusive range allows `u32::MAX` to be sampled where an
53/// exclusive range does not. If `end` is not passed in, it defaults to `u32::MAX`.
54///
55/// It is possible to pass in both `start` and `end`, just one of them, or neither in order to
56/// sample across the entire `u32` space.
57///
58/// # Example usage
59///
60/// ```edition2021
61/// use tera::{Context, Tera};
62/// use tera_rand::random_uint32;
63///
64/// let mut tera: Tera = Tera::default();
65/// tera.register_function("random_uint32", random_uint32);
66/// let context: Context = Context::new();
67///
68/// // bound by both start and end
69/// let rendered: String = tera
70///     .render_str("{{ random_uint32(start=49152, end=65535) }}", &context)
71///     .unwrap();
72/// // bound by just start
73/// let rendered: String = tera
74///     .render_str("{{ random_uint32(start=4294967290) }}", &context)
75///     .unwrap();
76/// // bound by just end
77/// let rendered: String = tera
78///     .render_str("{{ random_uint32(end=65535) }}", &context)
79///     .unwrap();
80/// // bound by neither start nor end
81/// let rendered: String = tera
82///     .render_str("{{ random_uint32() }}", &context)
83///     .unwrap();
84/// ```
85pub fn random_uint32(args: &HashMap<String, Value>) -> Result<Value> {
86    parse_range_and_gen_value_in_range(args, u32::MIN, u32::MAX)
87}
88
89/// A Tera function to generate a random unsigned 64-bit integer.
90///
91/// The `start` parameter takes an unsigned 64-bit integer to indicate the beginning of the
92/// range (inclusive). If `start` is not passed in, it defaults to `u64::MIN`.
93///
94/// The `end` parameter also takes an unsigned 64-bit integer indicating the end of the range,
95/// which is also inclusive. An inclusive range allows `u64::MAX` to be sampled where an
96/// exclusive range does not. If `end` is not passed in, it defaults to `u64::MAX`.
97///
98/// It is possible to pass in both `start` and `end`, just one of them, or neither in order to
99/// sample across the entire `u64` space.
100///
101/// # Example usage
102///
103/// ```edition2021
104/// use tera::{Context, Tera};
105/// use tera_rand::random_uint64;
106///
107/// let mut tera: Tera = Tera::default();
108/// tera.register_function("random_uint64", random_uint64);
109/// let context: Context = Context::new();
110///
111/// // bound by both start and end
112/// let rendered: String = tera
113///     .render_str("{{ random_uint64(start=49152, end=65535) }}", &context)
114///     .unwrap();
115/// // bound by just start
116/// let rendered: String = tera
117///     .render_str("{{ random_uint64(start=4294967296) }}", &context)
118///     .unwrap();
119/// // bound by just end
120/// let rendered: String = tera
121///     .render_str("{{ random_uint64(end=65535) }}", &context)
122///     .unwrap();
123/// // bound by neither start nor end
124/// let rendered: String = tera
125///     .render_str("{{ random_uint64() }}", &context)
126///     .unwrap();
127/// ```
128pub fn random_uint64(args: &HashMap<String, Value>) -> Result<Value> {
129    parse_range_and_gen_value_in_range(args, u64::MIN, u64::MAX)
130}
131
132/// A Tera function to generate a random signed 32-bit integer.
133///
134/// The `start` parameter takes a signed 32-bit integer to indicate the beginning of the
135/// range (inclusive). If `start` is not passed in, it defaults to `i32::MIN`.
136///
137/// The `end` parameter also takes an signed 32-bit integer indicating the end of the range,
138/// which is also inclusive. An inclusive range allows `i32::MAX` to be sampled where an
139/// exclusive range does not. If `end` is not passed in, it defaults to `i32::MAX`.
140///
141/// It is possible to pass in both `start` and `end`, just one of them, or neither in order to
142/// sample across the entire `i32` space.
143///
144/// # Example usage
145///
146/// ```edition2021
147/// use tera::{Context, Tera};
148/// use tera_rand::random_int32;
149///
150/// let mut tera: Tera = Tera::default();
151/// tera.register_function("random_int32", random_int32);
152/// let context: Context = Context::new();
153///
154/// // bound by both start and end
155/// let rendered: String = tera
156///     .render_str("{{ random_int32(start=-128, end=127) }}", &context)
157///     .unwrap();
158/// // bound by just start
159/// let rendered: String = tera
160///     .render_str("{{ random_int32(start=1) }}", &context)
161///     .unwrap();
162/// // bound by just end
163/// let rendered: String = tera
164///     .render_str("{{ random_int32(end=-1) }}", &context)
165///     .unwrap();
166/// // bound by neither start nor end
167/// let rendered: String = tera
168///     .render_str("{{ random_int32() }}", &context)
169///     .unwrap();
170/// ```
171pub fn random_int32(args: &HashMap<String, Value>) -> Result<Value> {
172    parse_range_and_gen_value_in_range(args, i32::MIN, i32::MAX)
173}
174
175/// A Tera function to generate a random signed 64-bit integer.
176///
177/// The `start` parameter takes a signed 64-bit integer to indicate the beginning of the
178/// range (inclusive). If `start` is not passed in, it defaults to `i64::MIN`.
179///
180/// The `end` parameter also takes an signed 64-bit integer indicating the end of the range,
181/// which is also inclusive. An inclusive range allows `i64::MAX` to be sampled where an
182/// exclusive range does not. If `end` is not passed in, it defaults to `i64::MAX`.
183///
184/// It is possible to pass in both `start` and `end`, just one of them, or neither in order to
185/// sample across the entire `i64` space.
186///
187/// # Example usage
188///
189/// ```edition2021
190/// use tera::{Context, Tera};
191/// use tera_rand::random_int64;
192///
193/// let mut tera: Tera = Tera::default();
194/// tera.register_function("random_int64", random_int64);
195/// let context: Context = Context::new();
196///
197/// // bound by both start and end
198/// let rendered: String = tera
199///     .render_str("{{ random_int64(start=-128, end=127) }}", &context)
200///     .unwrap();
201/// // bound by just start
202/// let rendered: String = tera
203///     .render_str("{{ random_int64(start=-1) }}", &context)
204///     .unwrap();
205/// // bound by just end
206/// let rendered: String = tera
207///     .render_str("{{ random_int64(end=0) }}", &context)
208///     .unwrap();
209/// // bound by neither start nor end
210/// let rendered: String = tera
211///     .render_str("{{ random_int64() }}", &context)
212///     .unwrap();
213/// ```
214pub fn random_int64(args: &HashMap<String, Value>) -> Result<Value> {
215    parse_range_and_gen_value_in_range(args, i64::MIN, i64::MAX)
216}
217
218/// A Tera function to generate a random 32-bit float.
219///
220/// By default, it generates a float between `0.0` and `1.0`.
221///
222/// The `start` parameter takes a 32-bit float to indicate the beginning of the
223/// range (inclusive). If `start` is not passed in, it defaults to `0.0`.
224///
225/// The `end` parameter also takes a 32-bit float indicating the end of the range,
226/// which is inclusive in order to remain consistent with the rest of the Tera functions.
227/// If `end` is not passed in, it defaults to `1.0`.
228///
229/// It is possible to pass in both `start` and `end`, just one of them, or neither.
230///
231/// # Example usage
232///
233/// ```edition2021
234/// use tera::{Context, Tera};
235/// use tera_rand::random_float32;
236///
237/// let mut tera: Tera = Tera::default();
238/// tera.register_function("random_float32", random_float32);
239/// let context: Context = Context::new();
240///
241/// // bound by both start and end
242/// let rendered: String = tera
243///     .render_str("{{ random_float32(start=-4096.0, end=4096.0) }}", &context)
244///     .unwrap();
245/// // bound by just start
246/// let rendered: String = tera
247///     .render_str("{{ random_float32(start=0.0) }}", &context)
248///     .unwrap();
249/// // bound by just end
250/// let rendered: String = tera
251///     .render_str("{{ random_float32(end=0.0) }}", &context)
252///     .unwrap();
253/// // bound by neither start nor end
254/// let rendered: String = tera
255///     .render_str("{{ random_float32() }}", &context)
256///     .unwrap();
257/// ```
258pub fn random_float32(args: &HashMap<String, Value>) -> Result<Value> {
259    parse_range_and_gen_value_in_range(args, 0.0, 1.0)
260}
261
262/// A Tera function to generate a random 64-bit float.
263///
264/// By default, it generates a float between `0.0` and `1.0`.
265///
266/// The `start` parameter takes a 64-bit float to indicate the beginning of the
267/// range (inclusive). If `start` is not passed in, it defaults to `0.0`.
268///
269/// The `end` parameter also takes a 64-bit float indicating the end of the range,
270/// which is inclusive in order to remain consistent with the rest of the Tera functions.
271/// If `end` is not passed in, it defaults to `1.0`.
272///
273/// It is possible to pass in both `start` and `end`, just one of them, or neither.
274///
275/// # Example usage
276///
277/// ```edition2021
278/// use tera::{Context, Tera};
279/// use tera_rand::random_float64;
280///
281/// let mut tera: Tera = Tera::default();
282/// tera.register_function("random_float64", random_float64);
283/// let context: Context = Context::new();
284///
285/// // bound by both start and end
286/// let rendered: String = tera
287///     .render_str("{{ random_float64(start=-4096.0, end=4096.0) }}", &context)
288///     .unwrap();
289/// // bound by just start
290/// let rendered: String = tera
291///     .render_str("{{ random_float64(start=0.0) }}", &context)
292///     .unwrap();
293/// // bound by just end
294/// let rendered: String = tera
295///     .render_str("{{ random_float64(end=0.0) }}", &context)
296///     .unwrap();
297/// // bound by neither start nor end
298/// let rendered: String = tera
299///     .render_str("{{ random_float64() }}", &context)
300///     .unwrap();
301/// ```
302pub fn random_float64(args: &HashMap<String, Value>) -> Result<Value> {
303    parse_range_and_gen_value_in_range(args, 0.0, 1.0)
304}
305
306#[cfg(test)]
307mod tests {
308    use crate::common::tests::test_tera_rand_function;
309    use crate::primitives::*;
310    use tracing_test::traced_test;
311
312    #[test]
313    #[traced_test]
314    fn test_random_bool() {
315        test_tera_rand_function(
316            random_bool,
317            "random_bool",
318            r#"{ "some_field": {{ random_bool() }} }"#,
319            r#"\{ "some_field": (true|false) }"#,
320        );
321    }
322
323    #[test]
324    #[traced_test]
325    fn test_random_char() {
326        test_tera_rand_function(
327            random_char,
328            "random_char",
329            r#"{ "some_field": {{ random_char() }} }"#,
330            r#"\{ "some_field": . }"#,
331        );
332    }
333
334    // uint32
335    #[test]
336    #[traced_test]
337    fn test_random_uint32() {
338        test_tera_rand_function(
339            random_uint32,
340            "random_uint32",
341            r#"{ "some_field": {{ random_uint32() }} }"#,
342            r#"\{ "some_field": \d+ }"#,
343        );
344    }
345
346    #[test]
347    #[traced_test]
348    fn test_random_uint32_near_min() {
349        test_tera_rand_function(
350            random_uint32,
351            "random_uint32",
352            r#"{ "some_field": {{ random_uint32(start=0, end=2) }} }"#,
353            r#"\{ "some_field": 0|1|2 }"#,
354        );
355    }
356
357    #[test]
358    #[traced_test]
359    fn test_random_uint32_near_max() {
360        test_tera_rand_function(
361            random_uint32,
362            "random_uint32",
363            r#"{ "some_field": {{ random_uint32(start=4294967293, end=4294967295) }} }"#,
364            r#"\{ "some_field": 4294967293|4294967294|4294967295 }"#,
365        );
366    }
367
368    #[test]
369    #[traced_test]
370    fn test_random_uint32_with_start_only() {
371        test_tera_rand_function(
372            random_uint32,
373            "random_uint32",
374            r#"{ "some_field": {{ random_uint32(start=4294967293) }} }"#,
375            r#"\{ "some_field": 4294967293|4294967294|4294967295 }"#,
376        );
377    }
378
379    #[test]
380    #[traced_test]
381    fn test_random_uint32_with_end_only() {
382        test_tera_rand_function(
383            random_uint32,
384            "random_uint32",
385            r#"{ "some_field": {{ random_uint32(end=2) }} }"#,
386            r#"\{ "some_field": 0|1|2 }"#,
387        );
388    }
389
390    // uint64
391    #[test]
392    #[traced_test]
393    fn test_random_uint64() {
394        test_tera_rand_function(
395            random_uint64,
396            "random_uint64",
397            r#"{ "some_field": {{ random_uint64() }} }"#,
398            r#"\{ "some_field": \d+ }"#,
399        );
400    }
401
402    #[test]
403    #[traced_test]
404    fn test_random_uint64_near_min() {
405        test_tera_rand_function(
406            random_uint64,
407            "random_uint64",
408            r#"{ "some_field": {{ random_uint64(start=0, end=2) }} }"#,
409            r#"\{ "some_field": 0|1|2 }"#,
410        );
411    }
412
413    // TODO: unignore once Tera supports u64s in functions: https://github.com/Keats/tera/issues/851
414    #[test]
415    #[ignore]
416    #[traced_test]
417    fn test_random_uint64_near_max() {
418        test_tera_rand_function(
419            random_uint64,
420            "random_uint64",
421            r#"{ "some_field": {{ random_uint64(start=18446744073709551601, end=18446744073709551603) }} }"#,
422            r#"\{ "some_field": 18446744073709551601|18446744073709551602|18446744073709551603 }"#,
423        );
424    }
425
426    // TODO: unignore once Tera supports u64s in functions: https://github.com/Keats/tera/issues/851
427    #[test]
428    #[ignore]
429    #[traced_test]
430    fn test_random_uint64_with_start_only() {
431        test_tera_rand_function(
432            random_uint64,
433            "random_uint64",
434            r#"{ "some_field": {{ random_uint64(start=18446744073709551601) }} }"#,
435            r#"\{ "some_field": 18446744073709551601|18446744073709551602|18446744073709551603 }"#,
436        );
437    }
438
439    #[test]
440    #[traced_test]
441    fn test_random_uint64_with_end_only() {
442        test_tera_rand_function(
443            random_uint64,
444            "random_uint64",
445            r#"{ "some_field": {{ random_uint64(end=2) }} }"#,
446            r#"\{ "some_field": 0|1|2 }"#,
447        );
448    }
449
450    // int32
451    #[test]
452    #[traced_test]
453    fn test_random_int32() {
454        test_tera_rand_function(
455            random_int32,
456            "random_int32",
457            r#"{ "some_field": {{ random_int32() }} }"#,
458            r#"\{ "some_field": -?\d+ }"#,
459        );
460    }
461
462    // int32 in range
463    #[test]
464    #[traced_test]
465    fn test_random_int32_near_min() {
466        test_tera_rand_function(
467            random_int32,
468            "random_int32",
469            r#"{ "some_field": {{ random_int32(start=-2147483648, end=-2147483646) }} }"#,
470            r#"\{ "some_field": (-2147483648|-2147483647|-2147483646) }"#,
471        );
472    }
473
474    #[test]
475    #[traced_test]
476    fn test_random_int32_near_max() {
477        test_tera_rand_function(
478            random_int32,
479            "random_int32",
480            r#"{ "some_field": {{ random_int32(start=2147483645, end=2147483647) }} }"#,
481            r#"\{ "some_field": 2147483645|2147483646|2147483647 }"#,
482        );
483    }
484
485    #[test]
486    #[traced_test]
487    fn test_random_int32_with_start_only() {
488        test_tera_rand_function(
489            random_int32,
490            "random_int32",
491            r#"{ "some_field": {{ random_int32(start=2147483645) }} }"#,
492            r#"\{ "some_field": 2147483645|2147483646|2147483647 }"#,
493        );
494    }
495
496    #[test]
497    #[traced_test]
498    fn test_random_int32_with_end_only() {
499        test_tera_rand_function(
500            random_int32,
501            "random_int32",
502            r#"{ "some_field": {{ random_int32(end=-2147483646) }} }"#,
503            r#"\{ "some_field": (-2147483648|-2147483647|-2147483646) }"#,
504        );
505    }
506
507    // int64
508    #[test]
509    #[traced_test]
510    fn test_random_int64() {
511        test_tera_rand_function(
512            random_int64,
513            "random_int64",
514            r#"{ "some_field": {{ random_int64() }} }"#,
515            r#"\{ "some_field": -?\d+ }"#,
516        );
517    }
518
519    #[test]
520    #[traced_test]
521    fn test_random_int64_near_min() {
522        test_tera_rand_function(
523            random_int64,
524            "random_int64",
525            r#"{ "some_field": {{ random_int64(start=-9223372036854775808, end=-9223372036854775806) }} }"#,
526            r#"\{ "some_field": (-9223372036854775808|-9223372036854775807|-9223372036854775806) }"#,
527        );
528    }
529
530    #[test]
531    #[traced_test]
532    fn test_random_int64_near_max() {
533        test_tera_rand_function(
534            random_int64,
535            "random_int64",
536            r#"{ "some_field": {{ random_int64(start=9223372036854775805, end=9223372036854775807) }} }"#,
537            r#"\{ "some_field": 9223372036854775805|9223372036854775806|9223372036854775807 }"#,
538        );
539    }
540
541    #[test]
542    #[traced_test]
543    fn test_random_int64_with_start_only() {
544        test_tera_rand_function(
545            random_int64,
546            "random_int64",
547            r#"{ "some_field": {{ random_int64(start=9223372036854775805) }} }"#,
548            r#"\{ "some_field": 9223372036854775805|9223372036854775806|9223372036854775807 }"#,
549        );
550    }
551
552    #[test]
553    #[traced_test]
554    fn test_random_int64_with_end_only() {
555        test_tera_rand_function(
556            random_int64,
557            "random_int64",
558            r#"{ "some_field": {{ random_int64(end=-9223372036854775806) }} }"#,
559            r#"\{ "some_field": (-9223372036854775808|-9223372036854775807|-9223372036854775806) }"#,
560        );
561    }
562
563    // float32
564    #[test]
565    #[traced_test]
566    fn test_random_float32() {
567        test_tera_rand_function(
568            random_float32,
569            "random_float32",
570            r#"{ "some_field": {{ random_float32(start=-6.0, end=-5.0) }} }"#,
571            r#"\{ "some_field": -5\.\d+ }"#,
572        );
573    }
574
575    // float64
576    #[test]
577    #[traced_test]
578    fn test_random_float64() {
579        test_tera_rand_function(
580            random_float64,
581            "random_float64",
582            r#"{ "some_field": {{ random_float64(start=-6.0, end=-5.0) }} }"#,
583            r#"\{ "some_field": -5\.\d+ }"#,
584        );
585    }
586}