1use crate::math::utils::ensure_bounded;
2use nu_engine::command_prelude::*;
3
4#[derive(Clone)]
5pub struct MathFloor;
6
7impl Command for MathFloor {
8 fn name(&self) -> &str {
9 "math floor"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("math floor")
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 floor of a number (largest integer less than or equal to that number)."
28 }
29
30 fn search_terms(&self) -> Vec<&str> {
31 vec!["round down", "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 matches!(input, PipelineData::Empty) {
48 return Err(ShellError::PipelineEmpty { dst_span: head });
49 }
50 if let PipelineData::Value(
51 Value::Range {
52 ref val,
53 internal_span,
54 },
55 ..,
56 ) = input
57 {
58 ensure_bounded(val.as_ref(), internal_span, head)?;
59 }
60 input.map(move |value| operate(value, head), engine_state.signals())
61 }
62
63 fn run_const(
64 &self,
65 working_set: &StateWorkingSet,
66 call: &Call,
67 input: PipelineData,
68 ) -> Result<PipelineData, ShellError> {
69 let head = call.head;
70 if matches!(input, PipelineData::Empty) {
72 return Err(ShellError::PipelineEmpty { dst_span: head });
73 }
74 if let PipelineData::Value(
75 Value::Range {
76 ref val,
77 internal_span,
78 },
79 ..,
80 ) = input
81 {
82 ensure_bounded(val.as_ref(), internal_span, head)?;
83 }
84 input.map(
85 move |value| operate(value, head),
86 working_set.permanent().signals(),
87 )
88 }
89
90 fn examples(&self) -> Vec<Example> {
91 vec![Example {
92 description: "Apply the floor function to a list of numbers",
93 example: "[1.5 2.3 -3.1] | math floor",
94 result: Some(Value::list(
95 vec![Value::test_int(1), Value::test_int(2), Value::test_int(-4)],
96 Span::test_data(),
97 )),
98 }]
99 }
100}
101
102fn operate(value: Value, head: Span) -> Value {
103 let span = value.span();
104 match value {
105 Value::Int { .. } => value,
106 Value::Float { val, .. } => Value::int(val.floor() as i64, span),
107 Value::Error { .. } => value,
108 other => Value::error(
109 ShellError::OnlySupportsThisInputType {
110 exp_input_type: "numeric".into(),
111 wrong_type: other.get_type().to_string(),
112 dst_span: head,
113 src_span: other.span(),
114 },
115 head,
116 ),
117 }
118}
119
120#[cfg(test)]
121mod test {
122 use super::*;
123
124 #[test]
125 fn test_examples() {
126 use crate::test_examples;
127
128 test_examples(MathFloor {})
129 }
130}