vrl/stdlib/
floor.rs

1use crate::compiler::prelude::*;
2
3use super::util::round_to_precision;
4use std::sync::LazyLock;
5
6static DEFAULT_PRECISION: LazyLock<Value> = LazyLock::new(|| Value::Integer(0));
7
8static PARAMETERS: LazyLock<Vec<Parameter>> = LazyLock::new(|| {
9    vec![
10        Parameter::required(
11            "value",
12            kind::FLOAT | kind::INTEGER,
13            "The number to round down.",
14        ),
15        Parameter::optional(
16            "precision",
17            kind::INTEGER,
18            "The number of decimal places to round to.",
19        )
20        .default(&DEFAULT_PRECISION),
21    ]
22});
23
24fn floor(precision: Value, value: Value) -> Resolved {
25    let precision = precision.try_integer()?;
26
27    match value {
28        Value::Float(f) => Ok(Value::from_f64_or_zero(round_to_precision(
29            *f,
30            precision,
31            f64::floor,
32        ))),
33        value @ Value::Integer(_) => Ok(value),
34        value => Err(ValueError::Expected {
35            got: value.kind(),
36            expected: Kind::float() | Kind::integer(),
37        }
38        .into()),
39    }
40}
41
42#[derive(Clone, Copy, Debug)]
43pub struct Floor;
44
45impl Function for Floor {
46    fn identifier(&self) -> &'static str {
47        "floor"
48    }
49
50    fn usage(&self) -> &'static str {
51        "Rounds the `value` down to the specified `precision`."
52    }
53
54    fn category(&self) -> &'static str {
55        Category::Number.as_ref()
56    }
57
58    fn return_kind(&self) -> u16 {
59        kind::INTEGER | kind::FLOAT
60    }
61
62    fn return_rules(&self) -> &'static [&'static str] {
63        &[
64            "Returns an integer if `precision` is `0` (this is the default). Returns a float otherwise.",
65        ]
66    }
67
68    fn parameters(&self) -> &'static [Parameter] {
69        PARAMETERS.as_slice()
70    }
71
72    fn compile(
73        &self,
74        _state: &state::TypeState,
75        _ctx: &mut FunctionCompileContext,
76        arguments: ArgumentList,
77    ) -> Compiled {
78        let value = arguments.required("value");
79        let precision = arguments.optional("precision");
80
81        Ok(FloorFn { value, precision }.as_expr())
82    }
83
84    fn examples(&self) -> &'static [Example] {
85        &[
86            example! {
87                title: "Round a number down (without precision)",
88                source: "floor(9.8)",
89                result: Ok("9.0"),
90            },
91            example! {
92                title: "Round a number down (with precision)",
93                source: "floor(4.345, precision: 2)",
94                result: Ok("4.34"),
95            },
96        ]
97    }
98}
99
100#[derive(Clone, Debug)]
101struct FloorFn {
102    value: Box<dyn Expression>,
103    precision: Option<Box<dyn Expression>>,
104}
105
106impl FunctionExpression for FloorFn {
107    fn resolve(&self, ctx: &mut Context) -> Resolved {
108        let precision = self
109            .precision
110            .map_resolve_with_default(ctx, || DEFAULT_PRECISION.clone())?;
111        let value = self.value.resolve(ctx)?;
112
113        floor(precision, value)
114    }
115
116    fn type_def(&self, state: &state::TypeState) -> TypeDef {
117        match Kind::from(self.value.type_def(state)) {
118            v if v.is_float() || v.is_integer() => v.into(),
119            _ => Kind::integer().or_float().into(),
120        }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::value;
128
129    test_function![
130        floor => Floor;
131
132        lower {
133            args: func_args![value: 1234.2],
134            want: Ok(value!(1234.0)),
135            tdef: TypeDef::float(),
136        }
137
138        higher {
139            args: func_args![value: 1234.8],
140            want: Ok(value!(1234.0)),
141            tdef: TypeDef::float(),
142        }
143
144        exact {
145            args: func_args![value: 1234],
146            want: Ok(value!(1234)),
147            tdef: TypeDef::integer(),
148        }
149
150        precision {
151            args: func_args![value: 1234.39429,
152                             precision: 1],
153            want: Ok(value!(1234.3)),
154            tdef: TypeDef::float(),
155        }
156
157        bigger_precision {
158            args: func_args![value: 1234.56789,
159                             precision: 4],
160            want: Ok(value!(1234.5678)),
161            tdef: TypeDef::float(),
162        }
163
164        huge_number {
165            args: func_args![value: 9_876_543_210_123_456_789_098_765_432_101_234_567_890_987_654_321.987_654_321,
166                             precision: 5],
167            want: Ok(value!(9_876_543_210_123_456_789_098_765_432_101_234_567_890_987_654_321.987_65)),
168            tdef: TypeDef::float(),
169        }
170    ];
171}