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}