1use super::{NumberBytes, get_number_bytes};
2use nu_cmd_base::input_handler::{CmdArgument, operate};
3use nu_engine::command_prelude::*;
4
5#[derive(Clone)]
6pub struct BitsNot;
7
8#[derive(Clone, Copy)]
9struct Arguments {
10 signed: bool,
11 number_size: NumberBytes,
12}
13
14impl CmdArgument for Arguments {
15 fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
16 None
17 }
18}
19
20impl Command for BitsNot {
21 fn name(&self) -> &str {
22 "bits not"
23 }
24
25 fn signature(&self) -> Signature {
26 Signature::build("bits not")
27 .input_output_types(vec![
28 (Type::Int, Type::Int),
29 (Type::Binary, Type::Binary),
30 (
31 Type::List(Box::new(Type::Int)),
32 Type::List(Box::new(Type::Int)),
33 ),
34 (
35 Type::List(Box::new(Type::Binary)),
36 Type::List(Box::new(Type::Binary)),
37 ),
38 ])
39 .allow_variants_without_examples(true)
40 .switch(
41 "signed",
42 "always treat input number as a signed number",
43 Some('s'),
44 )
45 .named(
46 "number-bytes",
47 SyntaxShape::Int,
48 "the size of unsigned number in bytes, it can be 1, 2, 4, 8, auto",
49 Some('n'),
50 )
51 .category(Category::Bits)
52 }
53
54 fn description(&self) -> &str {
55 "Performs logical negation on each bit."
56 }
57
58 fn search_terms(&self) -> Vec<&str> {
59 vec!["negation"]
60 }
61
62 fn run(
63 &self,
64 engine_state: &EngineState,
65 stack: &mut Stack,
66 call: &Call,
67 input: PipelineData,
68 ) -> Result<PipelineData, ShellError> {
69 let head = call.head;
70 let signed = call.has_flag(engine_state, stack, "signed")?;
71 let number_bytes: Option<Spanned<usize>> =
72 call.get_flag(engine_state, stack, "number-bytes")?;
73 let number_size = get_number_bytes(number_bytes, head)?;
74
75 if let PipelineData::Empty = input {
77 return Err(ShellError::PipelineEmpty { dst_span: head });
78 }
79
80 let args = Arguments {
81 signed,
82 number_size,
83 };
84
85 operate(action, args, input, head, engine_state.signals())
86 }
87
88 fn examples(&self) -> Vec<Example<'_>> {
89 vec![
90 Example {
91 description: "Apply logical negation to a list of numbers",
92 example: "[4 3 2] | bits not",
93 result: Some(Value::list(
94 vec![
95 Value::test_int(251),
96 Value::test_int(252),
97 Value::test_int(253),
98 ],
99 Span::test_data(),
100 )),
101 },
102 Example {
103 description: "Apply logical negation to a list of numbers, treat input as 2 bytes number",
104 example: "[4 3 2] | bits not --number-bytes 2",
105 result: Some(Value::list(
106 vec![
107 Value::test_int(65531),
108 Value::test_int(65532),
109 Value::test_int(65533),
110 ],
111 Span::test_data(),
112 )),
113 },
114 Example {
115 description: "Apply logical negation to a list of numbers, treat input as signed number",
116 example: "[4 3 2] | bits not --signed",
117 result: Some(Value::list(
118 vec![
119 Value::test_int(-5),
120 Value::test_int(-4),
121 Value::test_int(-3),
122 ],
123 Span::test_data(),
124 )),
125 },
126 Example {
127 description: "Apply logical negation to binary data",
128 example: "0x[ff 00 7f] | bits not",
129 result: Some(Value::binary(vec![0x00, 0xff, 0x80], Span::test_data())),
130 },
131 ]
132 }
133}
134
135fn action(input: &Value, args: &Arguments, span: Span) -> Value {
136 let Arguments {
137 signed,
138 number_size,
139 } = *args;
140 match input {
141 Value::Int { val, .. } => {
142 let val = *val;
143 if signed || val < 0 {
144 Value::int(!val, span)
145 } else {
146 use NumberBytes::*;
147 let out_val = match number_size {
148 One => !val & 0x00_00_00_00_00_FF,
149 Two => !val & 0x00_00_00_00_FF_FF,
150 Four => !val & 0x00_00_FF_FF_FF_FF,
151 Eight => !val & 0x7F_FF_FF_FF_FF_FF,
152 Auto => {
153 if val <= 0xFF {
154 !val & 0x00_00_00_00_00_FF
155 } else if val <= 0xFF_FF {
156 !val & 0x00_00_00_00_FF_FF
157 } else if val <= 0xFF_FF_FF_FF {
158 !val & 0x00_00_FF_FF_FF_FF
159 } else {
160 !val & 0x7F_FF_FF_FF_FF_FF
161 }
162 }
163 };
164 Value::int(out_val, span)
165 }
166 }
167 Value::Binary { val, .. } => {
168 Value::binary(val.iter().copied().map(|b| !b).collect::<Vec<_>>(), span)
169 }
170 Value::Error { .. } => input.clone(),
172 other => Value::error(
173 ShellError::OnlySupportsThisInputType {
174 exp_input_type: "int or binary".into(),
175 wrong_type: other.get_type().to_string(),
176 dst_span: other.span(),
177 src_span: span,
178 },
179 span,
180 ),
181 }
182}
183
184#[cfg(test)]
185mod test {
186 use super::*;
187
188 #[test]
189 fn test_examples() {
190 use crate::test_examples;
191
192 test_examples(BitsNot {})
193 }
194}