formualizer_eval/builtins/
random.rs1use crate::args::ArgSchema;
3use crate::function::Function;
4use crate::traits::{ArgumentHandle, FunctionContext};
5use formualizer_common::{ExcelError, LiteralValue};
6use formualizer_macros::func_caps;
7use rand::Rng;
8
9#[derive(Debug)]
10pub struct RandFn;
11
12impl Function for RandFn {
13 func_caps!(VOLATILE);
14
15 fn name(&self) -> &'static str {
16 "RAND"
17 }
18 fn min_args(&self) -> usize {
19 0
20 }
21
22 fn eval_scalar<'a, 'b>(
23 &self,
24 _args: &'a [ArgumentHandle<'a, 'b>],
25 ctx: &dyn FunctionContext,
26 ) -> Result<LiteralValue, ExcelError> {
27 let mut rng = ctx.rng_for_current(self.function_salt());
28 Ok(LiteralValue::Number(rng.gen_range(0.0..1.0)))
29 }
30}
31
32pub fn register_builtins() {
33 crate::function_registry::register_function(std::sync::Arc::new(RandFn));
34 crate::function_registry::register_function(std::sync::Arc::new(RandBetweenFn));
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::traits::DefaultFunctionContext;
41 use crate::{interpreter::Interpreter, test_workbook::TestWorkbook};
42 use formualizer_parse::LiteralValue;
43
44 fn interp(wb: &TestWorkbook) -> Interpreter<'_> {
45 wb.interpreter()
46 }
47
48 #[test]
49 fn test_rand_caps() {
50 let rand_fn = RandFn;
51 let caps = rand_fn.caps();
52
53 assert!(caps.contains(crate::function::FnCaps::VOLATILE));
55
56 assert!(!caps.contains(crate::function::FnCaps::PURE));
58 }
59
60 #[test]
61 fn test_rand() {
62 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RandFn));
63 let ctx = interp(&wb);
64
65 let f = ctx.context.get_function("", "RAND").unwrap();
66 let fctx = DefaultFunctionContext::new(ctx.context, None);
67 let result = f.eval_scalar(&[], &fctx).unwrap();
68 match result {
69 LiteralValue::Number(n) => assert!((0.0..1.0).contains(&n)),
70 _ => panic!("Expected a number"),
71 }
72 }
73
74 #[test]
75 fn test_randbetween_basic() {
76 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RandBetweenFn));
77 let ctx = interp(&wb);
78 let f = ctx.context.get_function("", "RANDBETWEEN").unwrap();
79 let fctx = DefaultFunctionContext::new(ctx.context, None);
80 let lo = formualizer_parse::ASTNode::new(
82 formualizer_parse::ASTNodeType::Literal(LiteralValue::Int(1)),
83 None,
84 );
85 let hi = formualizer_parse::ASTNode::new(
86 formualizer_parse::ASTNodeType::Literal(LiteralValue::Int(3)),
87 None,
88 );
89 let args = vec![
90 ArgumentHandle::new(&lo, &ctx),
91 ArgumentHandle::new(&hi, &ctx),
92 ];
93 let v = f.eval_scalar(&args, &fctx).unwrap();
94 match v {
95 LiteralValue::Int(n) => assert!((1..=3).contains(&n)),
96 _ => panic!("Expected Int"),
97 }
98 }
99}
100
101#[derive(Debug)]
102pub struct RandBetweenFn;
103
104impl Function for RandBetweenFn {
105 func_caps!(VOLATILE);
106
107 fn name(&self) -> &'static str {
108 "RANDBETWEEN"
109 }
110 fn min_args(&self) -> usize {
111 2
112 }
113 fn arg_schema(&self) -> &'static [ArgSchema] {
114 &crate::builtins::utils::ARG_NUM_LENIENT_TWO[..]
115 }
116
117 fn eval_scalar<'a, 'b>(
118 &self,
119 args: &'a [ArgumentHandle<'a, 'b>],
120 ctx: &dyn FunctionContext,
121 ) -> Result<LiteralValue, ExcelError> {
122 let lo_v = args[0].value()?.into_owned();
124 let hi_v = args[1].value()?.into_owned();
125 let lo = match lo_v {
126 LiteralValue::Int(n) => n,
127 LiteralValue::Number(n) => n as i64,
128 _ => 0,
129 };
130 let hi = match hi_v {
131 LiteralValue::Int(n) => n,
132 LiteralValue::Number(n) => n as i64,
133 _ => 0,
134 };
135 if hi < lo {
136 return Err(ExcelError::new(formualizer_common::ExcelErrorKind::Num)
137 .with_message("RANDBETWEEN: hi < lo".to_string()));
138 }
139 let mut rng = ctx.rng_for_current(self.function_salt());
140 let n = if (hi - lo) as u64 == u64::MAX {
141 lo
142 } else {
143 rng.gen_range(lo..=hi)
144 };
145 Ok(LiteralValue::Int(n))
146 }
147}