1use crate::math::utils::ensure_bounded;
2use nu_engine::command_prelude::*;
3
4#[derive(Clone)]
5pub struct MathSqrt;
6
7impl Command for MathSqrt {
8 fn name(&self) -> &str {
9 "math sqrt"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("math sqrt")
14 .input_output_types(vec![
15 (Type::Number, Type::Float),
16 (
17 Type::List(Box::new(Type::Number)),
18 Type::List(Box::new(Type::Float)),
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 square root of the input number."
28 }
29
30 fn search_terms(&self) -> Vec<&str> {
31 vec!["square", "root"]
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 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 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: "Compute the square root of each number in a list",
81 example: "[9 16] | math sqrt",
82 result: Some(Value::list(
83 vec![Value::test_float(3.0), Value::test_float(4.0)],
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 { val, .. } => {
94 let squared = (val as f64).sqrt();
95 if squared.is_nan() {
96 return error_negative_sqrt(head, span);
97 }
98 Value::float(squared, span)
99 }
100 Value::Float { val, .. } => {
101 let squared = val.sqrt();
102 if squared.is_nan() {
103 return error_negative_sqrt(head, span);
104 }
105 Value::float(squared, span)
106 }
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
120fn error_negative_sqrt(head: Span, span: Span) -> Value {
121 Value::error(
122 ShellError::UnsupportedInput {
123 msg: String::from("Can't square root a negative number"),
124 input: "value originates from here".into(),
125 msg_span: head,
126 input_span: span,
127 },
128 span,
129 )
130}
131
132#[cfg(test)]
133mod test {
134 use super::*;
135
136 #[test]
137 fn test_examples() {
138 use crate::test_examples;
139
140 test_examples(MathSqrt {})
141 }
142}