1use crate::math::utils::run_with_elementwise;
2use nu_engine::command_prelude::*;
3
4#[derive(Clone)]
5pub struct MathCeil;
6
7impl Command for MathCeil {
8 fn name(&self) -> &str {
9 "math ceil"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("math ceil")
14 .input_output_types(vec![
15 (Type::Number, Type::Int),
16 (Type::Duration, Type::Duration),
17 (Type::Filesize, Type::Filesize),
18 (
19 Type::List(Box::new(Type::Number)),
20 Type::List(Box::new(Type::Int)),
21 ),
22 (
23 Type::List(Box::new(Type::Duration)),
24 Type::List(Box::new(Type::Duration)),
25 ),
26 (
27 Type::List(Box::new(Type::Filesize)),
28 Type::List(Box::new(Type::Filesize)),
29 ),
30 (Type::Range, Type::List(Box::new(Type::Number))),
31 (Type::record(), Type::record()),
32 ])
33 .rest(
34 "columns",
35 SyntaxShape::CellPath,
36 "The cell-paths/columns to operate on.",
37 )
38 .allow_variants_without_examples(true)
39 .category(Category::Math)
40 }
41
42 fn description(&self) -> &str {
43 "Returns the ceil of a number (smallest integer greater than or equal to that number)."
44 }
45
46 fn extra_description(&self) -> &str {
47 "Filesize and duration values are stored as integers in base units \
48 (bytes and nanoseconds). With no display unit to round against, \
49 `math ceil` is the identity function for those types."
50 }
51
52 fn search_terms(&self) -> Vec<&str> {
53 vec!["ceiling", "round up", "rounding", "integer"]
54 }
55
56 fn is_const(&self) -> bool {
57 true
58 }
59
60 fn run(
61 &self,
62 engine_state: &EngineState,
63 stack: &mut Stack,
64 call: &Call,
65 input: PipelineData,
66 ) -> Result<PipelineData, ShellError> {
67 let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
68 let head = call.head;
69 run_with_elementwise(
70 input,
71 cell_paths,
72 head,
73 engine_state.signals(),
74 true,
75 move |value| operate(value, head),
76 )
77 }
78
79 fn run_const(
80 &self,
81 working_set: &StateWorkingSet,
82 call: &Call,
83 input: PipelineData,
84 ) -> Result<PipelineData, ShellError> {
85 let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
86 let head = call.head;
87 run_with_elementwise(
88 input,
89 cell_paths,
90 head,
91 working_set.permanent().signals(),
92 true,
93 move |value| operate(value, head),
94 )
95 }
96
97 fn examples(&self) -> Vec<Example<'_>> {
98 vec![
99 Example {
100 description: "Apply the ceil function to a list of numbers.",
101 example: "[1.5 2.3 -3.1] | math ceil",
102 result: Some(Value::list(
103 vec![Value::test_int(2), Value::test_int(3), Value::test_int(-3)],
104 Span::test_data(),
105 )),
106 },
107 Example {
108 description: "Apply ceiling to list-valued columns in a record.",
109 example: "{alice: [1.2 2.7 3.5], bob: [4.1 5.9]} | math ceil",
110 result: Some(Value::test_record(record! {
111 "alice" => Value::list(
112 vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
113 Span::test_data(),
114 ),
115 "bob" => Value::list(
116 vec![Value::test_int(5), Value::test_int(6)],
117 Span::test_data(),
118 ),
119 })),
120 },
121 Example {
122 description: "Apply ceiling to a single column using a cell path.",
123 example: "{alice: [1.2 2.7 3.5], bob: [4.1 5.9]} | math ceil alice",
124 result: Some(Value::test_record(record! {
125 "alice" => Value::list(
126 vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
127 Span::test_data(),
128 ),
129 "bob" => Value::list(
130 vec![Value::test_float(4.1), Value::test_float(5.9)],
131 Span::test_data(),
132 ),
133 })),
134 },
135 Example {
136 description: "Filesize values are already whole bytes, so ceiling is a no-op.",
138 example: "2.1KB | math ceil",
139 result: Some(Value::test_filesize(2100)),
140 },
141 ]
142 }
143}
144
145fn operate(value: Value, head: Span) -> Value {
146 let span = value.span();
147 match value {
148 Value::Int { .. } | Value::Duration { .. } | Value::Filesize { .. } => value,
150 Value::Float { val, .. } => Value::int(val.ceil() as i64, span),
151 Value::Error { .. } => value,
152 other => Value::error(
153 ShellError::OnlySupportsThisInputType {
154 exp_input_type: crate::math::utils::NUMERIC_INPUT_TYPES.into(),
155 wrong_type: other.get_type().to_string(),
156 dst_span: head,
157 src_span: other.span(),
158 },
159 head,
160 ),
161 }
162}
163
164#[cfg(test)]
165mod test {
166 use super::*;
167
168 #[test]
169 fn test_examples() -> nu_test_support::Result {
170 nu_test_support::test().examples(MathCeil)
171 }
172}