1use crate::compiler::prelude::*;
2
3fn r#mod(value: Value, modulus: Value) -> Resolved {
4 let result = value.try_rem(modulus)?;
5 Ok(result)
6}
7
8#[derive(Clone, Copy, Debug)]
9pub struct Mod;
10
11impl Function for Mod {
12 fn identifier(&self) -> &'static str {
13 "mod"
14 }
15
16 fn usage(&self) -> &'static str {
17 "Calculates the remainder of `value` divided by `modulus`."
18 }
19
20 fn category(&self) -> &'static str {
21 Category::Number.as_ref()
22 }
23
24 fn internal_failure_reasons(&self) -> &'static [&'static str] {
25 &[
26 "`value` is not an integer or float.",
27 "`modulus` is not an integer or float.",
28 "`modulus` is equal to 0.",
29 ]
30 }
31
32 fn return_kind(&self) -> u16 {
33 kind::INTEGER | kind::FLOAT
34 }
35
36 fn parameters(&self) -> &'static [Parameter] {
37 const PARAMETERS: &[Parameter] = &[
38 Parameter::required(
39 "value",
40 kind::INTEGER | kind::FLOAT,
41 "The value the `modulus` is applied to.",
42 ),
43 Parameter::required(
44 "modulus",
45 kind::INTEGER | kind::FLOAT,
46 "The `modulus` value.",
47 ),
48 ];
49 PARAMETERS
50 }
51
52 fn examples(&self) -> &'static [Example] {
53 &[example! {
54 title: "Calculate the remainder of two integers",
55 source: "mod(5, 2)",
56 result: Ok("1"),
57 }]
58 }
59
60 fn compile(
61 &self,
62 _state: &state::TypeState,
63 _ctx: &mut FunctionCompileContext,
64 arguments: ArgumentList,
65 ) -> Compiled {
66 let value = arguments.required("value");
67 let modulus = arguments.required("modulus");
68 Ok(ModFn { value, modulus }.as_expr())
71 }
72}
73
74#[derive(Debug, Clone)]
75struct ModFn {
76 value: Box<dyn Expression>,
77 modulus: Box<dyn Expression>,
78}
79
80impl FunctionExpression for ModFn {
81 fn resolve(&self, ctx: &mut Context) -> Resolved {
82 let value = self.value.resolve(ctx)?;
83 let modulus = self.modulus.resolve(ctx)?;
84 r#mod(value, modulus)
85 }
86
87 fn type_def(&self, state: &state::TypeState) -> TypeDef {
88 match self.modulus.resolve_constant(state) {
90 Some(value) if value.is_float() || value.is_integer() => match value {
91 Value::Float(v) if v.is_normal() => TypeDef::float().infallible(),
92 Value::Float(_) => TypeDef::float().fallible(),
93 Value::Integer(v) if v != 0 => TypeDef::integer().infallible(),
94 Value::Integer(_) => TypeDef::integer().fallible(),
95 _ => TypeDef::float().or_integer().fallible(),
96 },
97 _ => TypeDef::float().or_integer().fallible(),
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::value;
106
107 test_function![
108 r#mod => Mod;
109
110 int_mod {
111 args: func_args![value: 5, modulus: 2],
112 want: Ok(value!(1)),
113 tdef: TypeDef::integer().infallible(),
114 }
115
116 float_mod {
117 args: func_args![value: 5.0, modulus: 2.0],
118 want: Ok(value!(1.0)),
119 tdef: TypeDef::float().infallible(),
120 }
121
122 fallible_mod {
123 args: func_args![value: 5.0, modulus: {}],
124 want: Err("can't calculate remainder of type float and null"),
125 tdef: TypeDef::float().or_integer().fallible(),
126 }
127 ];
128}