nu_command/math/
ceil.rs

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        // This doesn't match explicit nulls
47        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        // This doesn't match explicit nulls
65        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}