From 0e2c888f73b9506fab3aaf2cb3164236a8cff4cf Mon Sep 17 00:00:00 2001 From: Justin Ma Date: Sat, 30 Jul 2022 20:34:11 +0800 Subject: [PATCH] Add bits root command and `bits and` command (#6188) --- crates/nu-command/src/bits/and.rs | 100 +++++++++++++++++++++++ crates/nu-command/src/bits/bits_.rs | 49 +++++++++++ crates/nu-command/src/bits/mod.rs | 4 + crates/nu-command/src/default_context.rs | 4 +- src/tests/test_bits.rs | 16 ++++ 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 crates/nu-command/src/bits/and.rs create mode 100644 crates/nu-command/src/bits/bits_.rs create mode 100644 src/tests/test_bits.rs diff --git a/crates/nu-command/src/bits/and.rs b/crates/nu-command/src/bits/and.rs new file mode 100644 index 0000000000..f7e3a995c6 --- /dev/null +++ b/crates/nu-command/src/bits/and.rs @@ -0,0 +1,100 @@ +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, +}; + +#[derive(Clone)] +pub struct SubCommand; + +impl Command for SubCommand { + fn name(&self) -> &str { + "bits and" + } + + fn signature(&self) -> Signature { + Signature::build("bits and") + .required( + "target", + SyntaxShape::Int, + "target integer to perform bit and", + ) + .category(Category::Bits) + } + + fn usage(&self) -> &str { + "Performs bitwise and for integers" + } + + fn search_terms(&self) -> Vec<&str> { + vec!["logic and"] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let head = call.head; + let target: i64 = call.req(engine_state, stack, 0)?; + + input.map( + move |value| operate(value, target, head), + engine_state.ctrlc.clone(), + ) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Apply bits and to two numbers", + example: "2 | bits and 2", + result: Some(Value::Int { + val: 2, + span: Span::test_data(), + }), + }, + Example { + description: "Apply logical and to a list of numbers", + example: "[4 3 2] | bits and 2", + result: Some(Value::List { + vals: vec![Value::test_int(0), Value::test_int(2), Value::test_int(2)], + span: Span::test_data(), + }), + }, + ] + } +} + +fn operate(value: Value, target: i64, head: Span) -> Value { + match value { + Value::Int { val, span } => Value::Int { + val: val & target, + span, + }, + other => Value::Error { + error: ShellError::UnsupportedInput( + format!( + "Only integer values are supported, input type: {:?}", + other.get_type() + ), + other.span().unwrap_or(head), + ), + }, + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-command/src/bits/bits_.rs b/crates/nu-command/src/bits/bits_.rs new file mode 100644 index 0000000000..1fda4f0d16 --- /dev/null +++ b/crates/nu-command/src/bits/bits_.rs @@ -0,0 +1,49 @@ +use nu_engine::get_full_help; +use nu_protocol::{ + ast::Call, + engine::{Command, EngineState, Stack}, + Category, IntoPipelineData, PipelineData, Signature, Value, +}; + +#[derive(Clone)] +pub struct Bits; + +impl Command for Bits { + fn name(&self) -> &str { + "bits" + } + + fn signature(&self) -> Signature { + Signature::build("bits").category(Category::Bits) + } + + fn usage(&self) -> &str { + "Various commands for working with bits" + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + Ok(Value::String { + val: get_full_help(&Bits.signature(), &Bits.examples(), engine_state, stack), + span: call.head, + } + .into_pipeline_data()) + } +} + +#[cfg(test)] +mod test { + use crate::Bits; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(Bits {}) + } +} diff --git a/crates/nu-command/src/bits/mod.rs b/crates/nu-command/src/bits/mod.rs index 7d299c9bf4..30c27debcf 100644 --- a/crates/nu-command/src/bits/mod.rs +++ b/crates/nu-command/src/bits/mod.rs @@ -1,3 +1,7 @@ +mod and; +mod bits_; mod not; +pub use and::SubCommand as BitsAnd; +pub use bits_::Bits; pub use not::SubCommand as BitsNot; diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 381fe95824..c281025b1a 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -209,7 +209,9 @@ pub fn create_default_context() -> EngineState { // Bits bind_command! { - BitsNot + Bits, + BitsAnd, + BitsNot, } // Bytes diff --git a/src/tests/test_bits.rs b/src/tests/test_bits.rs new file mode 100644 index 0000000000..eae60bcdf3 --- /dev/null +++ b/src/tests/test_bits.rs @@ -0,0 +1,16 @@ +use crate::tests::{fail_test, run_test, TestResult}; + +#[test] +fn bits_and() -> TestResult { + run_test("2 | bits and 4", "0") +} + +#[test] +fn bits_and_negative() -> TestResult { + run_test("-3 | bits and 5", "5") +} + +#[test] +fn bits_and_list() -> TestResult { + run_test("[1 2 3 8 9 10] | bits and 2 | str collect", "022002") +}