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}