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}