nu_cmd_extra/extra/bits/
not.rs

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        // This doesn't match explicit nulls
76        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        // Propagate errors by explicitly matching them before the final case.
171        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}