1use crate::math::utils::ensure_bounded;
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 (
17 Type::List(Box::new(Type::Number)),
18 Type::List(Box::new(Type::Int)),
19 ),
20 (Type::Range, Type::List(Box::new(Type::Number))),
21 ])
22 .allow_variants_without_examples(true)
23 .category(Category::Math)
24 }
25
26 fn description(&self) -> &str {
27 "Returns the ceil of a number (smallest integer greater than or equal to that number)."
28 }
29
30 fn search_terms(&self) -> Vec<&str> {
31 vec!["ceiling", "round up", "rounding", "integer"]
32 }
33
34 fn is_const(&self) -> bool {
35 true
36 }
37
38 fn run(
39 &self,
40 engine_state: &EngineState,
41 _stack: &mut Stack,
42 call: &Call,
43 input: PipelineData,
44 ) -> Result<PipelineData, ShellError> {
45 let head = call.head;
46 if let PipelineData::Empty = input {
48 return Err(ShellError::PipelineEmpty { dst_span: head });
49 }
50 if let PipelineData::Value(ref v @ Value::Range { ref val, .. }, ..) = input {
51 let span = v.span();
52 ensure_bounded(val, span, head)?;
53 }
54 input.map(move |value| operate(value, head), engine_state.signals())
55 }
56
57 fn run_const(
58 &self,
59 working_set: &StateWorkingSet,
60 call: &Call,
61 input: PipelineData,
62 ) -> Result<PipelineData, ShellError> {
63 let head = call.head;
64 if let PipelineData::Empty = input {
66 return Err(ShellError::PipelineEmpty { dst_span: head });
67 }
68 if let PipelineData::Value(ref v @ Value::Range { ref val, .. }, ..) = input {
69 let span = v.span();
70 ensure_bounded(val, span, head)?;
71 }
72 input.map(
73 move |value| operate(value, head),
74 working_set.permanent().signals(),
75 )
76 }
77
78 fn examples(&self) -> Vec<Example<'_>> {
79 vec![Example {
80 description: "Apply the ceil function to a list of numbers",
81 example: "[1.5 2.3 -3.1] | math ceil",
82 result: Some(Value::list(
83 vec![Value::test_int(2), Value::test_int(3), Value::test_int(-3)],
84 Span::test_data(),
85 )),
86 }]
87 }
88}
89
90fn operate(value: Value, head: Span) -> Value {
91 let span = value.span();
92 match value {
93 Value::Int { .. } => value,
94 Value::Float { val, .. } => Value::int(val.ceil() as i64, span),
95 Value::Error { .. } => value,
96 other => Value::error(
97 ShellError::OnlySupportsThisInputType {
98 exp_input_type: "numeric".into(),
99 wrong_type: other.get_type().to_string(),
100 dst_span: head,
101 src_span: other.span(),
102 },
103 head,
104 ),
105 }
106}
107
108#[cfg(test)]
109mod test {
110 use super::*;
111
112 #[test]
113 fn test_examples() {
114 use crate::test_examples;
115
116 test_examples(MathCeil {})
117 }
118}