nu_command/math/
abs.rs

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(
52            Value::Range {
53                ref val,
54                internal_span,
55            },
56            ..,
57        ) = input
58        {
59            ensure_bounded(val.as_ref(), internal_span, head)?;
60        }
61        input.map(move |value| abs_helper(value, head), engine_state.signals())
62    }
63
64    fn run_const(
65        &self,
66        working_set: &StateWorkingSet,
67        call: &Call,
68        input: PipelineData,
69    ) -> Result<PipelineData, ShellError> {
70        let head = call.head;
71        if let PipelineData::Value(
72            Value::Range {
73                ref val,
74                internal_span,
75            },
76            ..,
77        ) = input
78        {
79            ensure_bounded(val.as_ref(), internal_span, head)?;
80        }
81        input.map(
82            move |value| abs_helper(value, head),
83            working_set.permanent().signals(),
84        )
85    }
86
87    fn examples(&self) -> Vec<Example> {
88        vec![Example {
89            description: "Compute absolute value of each number in a list of numbers",
90            example: "[-50 -100.0 25] | math abs",
91            result: Some(Value::list(
92                vec![
93                    Value::test_int(50),
94                    Value::test_float(100.0),
95                    Value::test_int(25),
96                ],
97                Span::test_data(),
98            )),
99        }]
100    }
101}
102
103fn abs_helper(val: Value, head: Span) -> Value {
104    let span = val.span();
105    match val {
106        Value::Int { val, .. } => Value::int(val.abs(), span),
107        Value::Float { val, .. } => Value::float(val.abs(), span),
108        Value::Duration { val, .. } => Value::duration(val.abs(), span),
109        Value::Error { .. } => val,
110        other => Value::error(
111            ShellError::OnlySupportsThisInputType {
112                exp_input_type: "numeric".into(),
113                wrong_type: other.get_type().to_string(),
114                dst_span: head,
115                src_span: other.span(),
116            },
117            head,
118        ),
119    }
120}
121
122#[cfg(test)]
123mod test {
124    use super::*;
125
126    #[test]
127    fn test_examples() {
128        use crate::test_examples;
129
130        test_examples(MathAbs {})
131    }
132}