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