vrl/stdlib/
ceil.rs

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