vrl/stdlib/
random_float.rs1use crate::compiler::prelude::*;
2use rand::{Rng, thread_rng};
3use std::ops::Range;
4
5const INVALID_RANGE_ERR: &str = "max must be greater than min";
6
7fn random_float(min: Value, max: Value) -> Resolved {
8 let min = min.try_float()?;
9 let max = max.try_float()?;
10
11 if max <= min {
12 return Err("max must be greater than min".into());
13 }
14
15 let f: f64 = thread_rng().gen_range(min..max);
16
17 Ok(Value::Float(NotNan::new(f).expect("always a number")))
18}
19
20fn get_range(min: Value, max: Value) -> std::result::Result<Range<f64>, &'static str> {
21 let min = min.try_float().expect("min must be a float");
22 let max = max.try_float().expect("max must be a float");
23
24 if max <= min {
25 return Err(INVALID_RANGE_ERR);
26 }
27
28 Ok(min..max)
29}
30
31#[derive(Clone, Copy, Debug)]
32pub struct RandomFloat;
33
34impl Function for RandomFloat {
35 fn identifier(&self) -> &'static str {
36 "random_float"
37 }
38
39 fn usage(&self) -> &'static str {
40 "Returns a random float between [min, max)."
41 }
42
43 fn category(&self) -> &'static str {
44 Category::Random.as_ref()
45 }
46
47 fn internal_failure_reasons(&self) -> &'static [&'static str] {
48 &["`max` is not greater than `min`."]
49 }
50
51 fn return_kind(&self) -> u16 {
52 kind::FLOAT
53 }
54
55 fn parameters(&self) -> &'static [Parameter] {
56 const PARAMETERS: &[Parameter] = &[
57 Parameter::required("min", kind::FLOAT, "Minimum value (inclusive)."),
58 Parameter::required("max", kind::FLOAT, "Maximum value (exclusive)."),
59 ];
60 PARAMETERS
61 }
62
63 fn examples(&self) -> &'static [Example] {
64 &[example! {
65 title: "Random float from 0.0 to 10.0, not including 10.0",
66 source: indoc! {"
67 f = random_float(0.0, 10.0)
68 f >= 0 && f < 10
69 "},
70 result: Ok("true"),
71 }]
72 }
73
74 fn compile(
75 &self,
76 state: &state::TypeState,
77 _ctx: &mut FunctionCompileContext,
78 arguments: ArgumentList,
79 ) -> Compiled {
80 let min = arguments.required("min");
81 let max = arguments.required("max");
82
83 if let (Some(min), Some(max)) = (min.resolve_constant(state), max.resolve_constant(state)) {
84 let _: Range<f64> =
86 get_range(min, max.clone()).map_err(|err| function::Error::InvalidArgument {
87 keyword: "max",
88 value: max,
89 error: err,
90 })?;
91 }
92
93 Ok(RandomFloatFn { min, max }.as_expr())
94 }
95}
96
97#[derive(Debug, Clone)]
98struct RandomFloatFn {
99 min: Box<dyn Expression>,
100 max: Box<dyn Expression>,
101}
102
103impl FunctionExpression for RandomFloatFn {
104 fn resolve(&self, ctx: &mut Context) -> Resolved {
105 let min = self.min.resolve(ctx)?;
106 let max = self.max.resolve(ctx)?;
107
108 random_float(min, max)
109 }
110
111 fn type_def(&self, state: &state::TypeState) -> TypeDef {
112 match (
113 self.min.resolve_constant(state),
114 self.max.resolve_constant(state),
115 ) {
116 (Some(min), Some(max)) => {
117 if get_range(min, max).is_ok() {
118 TypeDef::float().infallible()
119 } else {
120 TypeDef::float().fallible()
121 }
122 }
123 _ => TypeDef::float().fallible(),
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::value;
132 test_function![
135 random_float => RandomFloat;
136
137 bad_range {
138 args: func_args![min: value!(1.0), max: value!(1.0)],
139 want: Err("invalid argument"),
140 tdef: TypeDef::float().fallible(),
141 }
142 ];
143}