1use nu_engine::command_prelude::*;
2use nu_protocol::ast::{Assignment, Bits, Boolean, Comparison, Math, Operator};
3use strum::IntoEnumIterator;
4
5#[derive(Clone)]
6pub struct HelpOperators;
7
8impl Command for HelpOperators {
9 fn name(&self) -> &str {
10 "help operators"
11 }
12
13 fn description(&self) -> &str {
14 "Show help on nushell operators."
15 }
16
17 fn signature(&self) -> Signature {
18 Signature::build("help operators")
19 .category(Category::Core)
20 .input_output_types(vec![(Type::Nothing, Type::table())])
21 .allow_variants_without_examples(true)
22 }
23
24 fn run(
25 &self,
26 _engine_state: &EngineState,
27 _stack: &mut Stack,
28 call: &Call,
29 _input: PipelineData,
30 ) -> Result<PipelineData, ShellError> {
31 let head = call.head;
32 let mut operators = Assignment::iter()
33 .map(Operator::Assignment)
34 .chain(Comparison::iter().map(Operator::Comparison))
35 .chain(Math::iter().map(Operator::Math))
36 .chain(Bits::iter().map(Operator::Bits))
37 .chain(Boolean::iter().map(Operator::Boolean))
38 .map(|op| {
39 if op == Operator::Comparison(Comparison::RegexMatch) {
40 Value::record(
41 record! {
42 "type" => Value::string(op_type(&op), head),
43 "operator" => Value::string("=~, like", head),
44 "name" => Value::string(name(&op), head),
45 "description" => Value::string(description(&op), head),
46 "precedence" => Value::int(op.precedence().into(), head),
47 },
48 head,
49 )
50 } else if op == Operator::Comparison(Comparison::NotRegexMatch) {
51 Value::record(
52 record! {
53 "type" => Value::string(op_type(&op), head),
54 "operator" => Value::string("!~, not-like", head),
55 "name" => Value::string(name(&op), head),
56 "description" => Value::string(description(&op), head),
57 "precedence" => Value::int(op.precedence().into(), head),
58 },
59 head,
60 )
61 } else {
62 Value::record(
63 record! {
64 "type" => Value::string(op_type(&op), head),
65 "operator" => Value::string(op.to_string(), head),
66 "name" => Value::string(name(&op), head),
67 "description" => Value::string(description(&op), head),
68 "precedence" => Value::int(op.precedence().into(), head),
69 },
70 head,
71 )
72 }
73 })
74 .collect::<Vec<_>>();
75
76 operators.push(Value::record(
77 record! {
78 "type" => Value::string("Boolean", head),
79 "operator" => Value::string("not", head),
80 "name" => Value::string("Not", head),
81 "description" => Value::string("Negates a value or expression.", head),
82 "precedence" => Value::int(55, head),
83 },
84 head,
85 ));
86
87 Ok(Value::list(operators, head).into_pipeline_data())
88 }
89}
90
91fn op_type(operator: &Operator) -> &'static str {
92 match operator {
93 Operator::Comparison(_) => "Comparison",
94 Operator::Math(_) => "Math",
95 Operator::Boolean(_) => "Boolean",
96 Operator::Bits(_) => "Bitwise",
97 Operator::Assignment(_) => "Assignment",
98 }
99}
100
101fn name(operator: &Operator) -> String {
102 match operator {
103 Operator::Comparison(op) => format!("{op:?}"),
104 Operator::Math(op) => format!("{op:?}"),
105 Operator::Boolean(op) => format!("{op:?}"),
106 Operator::Bits(op) => format!("{op:?}"),
107 Operator::Assignment(op) => format!("{op:?}"),
108 }
109}
110
111fn description(operator: &Operator) -> &'static str {
112 match operator {
113 Operator::Comparison(Comparison::Equal) => "Checks if two values are equal.",
114 Operator::Comparison(Comparison::NotEqual) => "Checks if two values are not equal.",
115 Operator::Comparison(Comparison::LessThan) => "Checks if a value is less than another.",
116 Operator::Comparison(Comparison::GreaterThan) => {
117 "Checks if a value is greater than another."
118 }
119 Operator::Comparison(Comparison::LessThanOrEqual) => {
120 "Checks if a value is less than or equal to another."
121 }
122 Operator::Comparison(Comparison::GreaterThanOrEqual) => {
123 "Checks if a value is greater than or equal to another."
124 }
125 Operator::Comparison(Comparison::RegexMatch) => {
126 "Checks if a value matches a regular expression."
127 }
128 Operator::Comparison(Comparison::NotRegexMatch) => {
129 "Checks if a value does not match a regular expression."
130 }
131 Operator::Comparison(Comparison::In) => {
132 "Checks if a value is in a list, is part of a string, or is a key in a record."
133 }
134 Operator::Comparison(Comparison::NotIn) => {
135 "Checks if a value is not in a list, is not part of a string, or is not a key in a record."
136 }
137 Operator::Comparison(Comparison::Has) => {
138 "Checks if a list contains a value, a string contains another, or if a record has a key."
139 }
140 Operator::Comparison(Comparison::NotHas) => {
141 "Checks if a list does not contain a value, a string does not contain another, or if a record does not have a key."
142 }
143 Operator::Comparison(Comparison::StartsWith) => "Checks if a string starts with another.",
144 Operator::Comparison(Comparison::NotStartsWith) => {
145 "Checks if a string does not start with another."
146 }
147 Operator::Comparison(Comparison::EndsWith) => "Checks if a string ends with another.",
148 Operator::Comparison(Comparison::NotEndsWith) => {
149 "Checks if a string does not end with another."
150 }
151 Operator::Math(Math::Add) => "Adds two values.",
152 Operator::Math(Math::Subtract) => "Subtracts two values.",
153 Operator::Math(Math::Multiply) => "Multiplies two values.",
154 Operator::Math(Math::Divide) => "Divides two values.",
155 Operator::Math(Math::FloorDivide) => "Divides two values and floors the result.",
156 Operator::Math(Math::Modulo) => "Divides two values and returns the remainder.",
157 Operator::Math(Math::Pow) => "Raises one value to the power of another.",
158 Operator::Math(Math::Concatenate) => {
159 "Concatenates two lists, two strings, or two binary values."
160 }
161 Operator::Boolean(Boolean::Or) => "Checks if either value is true.",
162 Operator::Boolean(Boolean::Xor) => "Checks if one value is true and the other is false.",
163 Operator::Boolean(Boolean::And) => "Checks if both values are true.",
164 Operator::Bits(Bits::BitOr) => "Performs a bitwise OR on two values.",
165 Operator::Bits(Bits::BitXor) => "Performs a bitwise XOR on two values.",
166 Operator::Bits(Bits::BitAnd) => "Performs a bitwise AND on two values.",
167 Operator::Bits(Bits::ShiftLeft) => "Bitwise shifts a value left by another.",
168 Operator::Bits(Bits::ShiftRight) => "Bitwise shifts a value right by another.",
169 Operator::Assignment(Assignment::Assign) => "Assigns a value to a variable.",
170 Operator::Assignment(Assignment::AddAssign) => "Adds a value to a variable.",
171 Operator::Assignment(Assignment::SubtractAssign) => "Subtracts a value from a variable.",
172 Operator::Assignment(Assignment::MultiplyAssign) => "Multiplies a variable by a value.",
173 Operator::Assignment(Assignment::DivideAssign) => "Divides a variable by a value.",
174 Operator::Assignment(Assignment::ConcatenateAssign) => {
175 "Concatenates a list, a string, or a binary value to a variable of the same type."
176 }
177 }
178}
179
180#[cfg(test)]
181mod test {
182 #[test]
183 fn test_examples() {
184 use super::HelpOperators;
185 use crate::test_examples;
186 test_examples(HelpOperators {})
187 }
188}