1use crate::math::utils::ensure_bounded;
2use nu_engine::command_prelude::*;
3
4#[derive(Clone)]
5pub struct MathAbs;
6
7impl Command for MathAbs {
8 fn name(&self) -> &str {
9 "math abs"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("math abs")
14 .input_output_types(vec![
15 (Type::Number, Type::Number),
16 (Type::Duration, Type::Duration),
17 (
18 Type::List(Box::new(Type::Number)),
19 Type::List(Box::new(Type::Number)),
20 ),
21 (
22 Type::List(Box::new(Type::Duration)),
23 Type::List(Box::new(Type::Duration)),
24 ),
25 (Type::Range, Type::List(Box::new(Type::Number))),
26 ])
27 .allow_variants_without_examples(true)
28 .category(Category::Math)
29 }
30
31 fn description(&self) -> &str {
32 "Returns the absolute value of a number."
33 }
34
35 fn search_terms(&self) -> Vec<&str> {
36 vec!["absolute", "modulus", "positive", "distance"]
37 }
38
39 fn is_const(&self) -> bool {
40 true
41 }
42
43 fn run(
44 &self,
45 engine_state: &EngineState,
46 _stack: &mut Stack,
47 call: &Call,
48 input: PipelineData,
49 ) -> Result<PipelineData, ShellError> {
50 let head = call.head;
51 if let PipelineData::Value(ref v @ Value::Range { ref val, .. }, ..) = input {
52 let span = v.span();
53 ensure_bounded(val, span, head)?;
54 }
55 input.map(move |value| abs_helper(value, head), engine_state.signals())
56 }
57
58 fn run_const(
59 &self,
60 working_set: &StateWorkingSet,
61 call: &Call,
62 input: PipelineData,
63 ) -> Result<PipelineData, ShellError> {
64 let head = call.head;
65 if let PipelineData::Value(ref v @ Value::Range { ref val, .. }, ..) = input {
66 let span = v.span();
67 ensure_bounded(val, span, head)?;
68 }
69 input.map(
70 move |value| abs_helper(value, head),
71 working_set.permanent().signals(),
72 )
73 }
74
75 fn examples(&self) -> Vec<Example<'_>> {
76 vec![Example {
77 description: "Compute absolute value of each number in a list of numbers",
78 example: "[-50 -100.0 25] | math abs",
79 result: Some(Value::list(
80 vec![
81 Value::test_int(50),
82 Value::test_float(100.0),
83 Value::test_int(25),
84 ],
85 Span::test_data(),
86 )),
87 }]
88 }
89}
90
91fn abs_helper(val: Value, head: Span) -> Value {
92 let span = val.span();
93 match val {
94 Value::Int { val, .. } => Value::int(val.abs(), span),
95 Value::Float { val, .. } => Value::float(val.abs(), span),
96 Value::Duration { val, .. } => Value::duration(val.abs(), span),
97 Value::Error { .. } => val,
98 other => Value::error(
99 ShellError::OnlySupportsThisInputType {
100 exp_input_type: "numeric".into(),
101 wrong_type: other.get_type().to_string(),
102 dst_span: head,
103 src_span: other.span(),
104 },
105 head,
106 ),
107 }
108}
109
110#[cfg(test)]
111mod test {
112 use super::*;
113
114 #[test]
115 fn test_examples() {
116 use crate::test_examples;
117
118 test_examples(MathAbs {})
119 }
120}