From ec9089ebc55e961ffe4a058043b103159267d2b8 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Tue, 23 Jul 2024 16:37:54 +0300 Subject: [PATCH 01/19] Initial commit --- Cargo.lock | 7 ++ crates/nu-command/Cargo.toml | 1 + crates/nu-command/src/default_context.rs | 2 + crates/nu-command/src/strings/base/decode.rs | 76 +++++++++++++++++ crates/nu-command/src/strings/base/encode.rs | 79 ++++++++++++++++++ crates/nu-command/src/strings/base/mod.rs | 88 ++++++++++++++++++++ crates/nu-command/src/strings/mod.rs | 2 + 7 files changed, 255 insertions(+) create mode 100644 crates/nu-command/src/strings/base/decode.rs create mode 100644 crates/nu-command/src/strings/base/encode.rs create mode 100644 crates/nu-command/src/strings/base/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 97f81d5185..94ec5cfee7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1147,6 +1147,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "deranged" version = "0.3.11" @@ -3044,6 +3050,7 @@ dependencies = [ "chrono-tz 0.8.6", "crossterm", "csv", + "data-encoding", "deunicode", "dialoguer", "digest", diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index ea501578b6..2af5192056 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -102,6 +102,7 @@ v_htmlescape = { workspace = true } wax = { workspace = true } which = { workspace = true } unicode-width = { workspace = true } +data-encoding = { version = "2.6.0", features = ["alloc"] } [target.'cfg(windows)'.dependencies] winreg = { workspace = true } diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 4adcfe9214..4cbce1003c 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -179,6 +179,8 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Char, Decode, Encode, + EncodeBase, + DecodeBase, DecodeBase64, EncodeBase64, DetectColumns, diff --git a/crates/nu-command/src/strings/base/decode.rs b/crates/nu-command/src/strings/base/decode.rs new file mode 100644 index 0000000000..af494750a6 --- /dev/null +++ b/crates/nu-command/src/strings/base/decode.rs @@ -0,0 +1,76 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct DecodeBase; + +impl Command for DecodeBase { + fn name(&self) -> &str { + "decode base" + } + + fn signature(&self) -> Signature { + Signature::build("decode base") + .input_output_types(vec![(Type::String, Type::Binary)]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Decode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req(engine_state, stack, 0)?; + + decode(&name, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req_const(working_set, 0)?; + + decode(&name, call.span(), input) + } +} + +fn decode(name: &str, call_span: Span, input: PipelineData) -> Result { + let encoding = super::encoding(&name, call_span, call_span)?; + let metadata = input.metadata(); + let (input_str, input_span) = super::get_string(input, call_span)?; + let output = encoding.decode(input_str.as_bytes()).unwrap(); + + Ok(Value::binary(output, call_span).into_pipeline_data_with_metadata(metadata)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(DecodeBase) + } +} diff --git a/crates/nu-command/src/strings/base/encode.rs b/crates/nu-command/src/strings/base/encode.rs new file mode 100644 index 0000000000..970ce3c429 --- /dev/null +++ b/crates/nu-command/src/strings/base/encode.rs @@ -0,0 +1,79 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct EncodeBase; + +impl Command for EncodeBase { + fn name(&self) -> &str { + "encode base" + } + + fn signature(&self) -> Signature { + Signature::build("encode base") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req(engine_state, stack, 0)?; + + encode(&name, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req_const(working_set, 0)?; + + encode(&name, call.span(), input) + } +} + +fn encode(name: &str, call_span: Span, input: PipelineData) -> Result { + let encoding = super::encoding(name, call_span, call_span)?; + let metadata = input.metadata(); + let (input_bytes, input_span) = super::get_binary(input, call_span)?; + let output = encoding.encode(&input_bytes); + + Ok(Value::string(output, call_span).into_pipeline_data_with_metadata(metadata)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(EncodeBase) + } +} diff --git a/crates/nu-command/src/strings/base/mod.rs b/crates/nu-command/src/strings/base/mod.rs new file mode 100644 index 0000000000..e14516eab9 --- /dev/null +++ b/crates/nu-command/src/strings/base/mod.rs @@ -0,0 +1,88 @@ +use nu_protocol::{PipelineData, ShellError, Span, Value}; + +mod decode; +mod encode; + +pub use decode::DecodeBase; +pub use encode::EncodeBase; + +pub fn encoding( + name: &str, + val_span: Span, + call_span: Span, +) -> Result { + match name { + "base32" => Ok(data_encoding::BASE32), + "base32hex" => Ok(data_encoding::BASE32HEX), + "base32hex_nopad" => Ok(data_encoding::BASE32HEX_NOPAD), + "base32_dnscurve" => Ok(data_encoding::BASE32_DNSCURVE), + "base32_dnssec" => Ok(data_encoding::BASE32_DNSSEC), + "base32_nopad" => Ok(data_encoding::BASE32_NOPAD), + "base64" => Ok(data_encoding::BASE64), + "base64url" => Ok(data_encoding::BASE64URL), + "base64url_nopad" => Ok(data_encoding::BASE64URL_NOPAD), + "base64_mime" => Ok(data_encoding::BASE64_MIME), + "base64_mime_permissive" => Ok(data_encoding::BASE64_MIME_PERMISSIVE), + "base64_nopad" => Ok(data_encoding::BASE64_NOPAD), + "hexlower" => Ok(data_encoding::HEXLOWER), + "hexlower_permissive" => Ok(data_encoding::HEXLOWER_PERMISSIVE), + "hexupper" => Ok(data_encoding::HEXUPPER), + "hexupper_permissive" => Ok(data_encoding::HEXUPPER_PERMISSIVE), + _ => Err(ShellError::IncorrectValue { + msg: format!("Encoding '{name}' not found"), + val_span, + call_span, + }), + } +} + +pub fn get_string(input: PipelineData, call_span: Span) -> Result<(String, Span), ShellError> { + match input { + PipelineData::Value(val, ..) => { + let span = val.span(); + match val { + Value::String { val, .. } => Ok((val, span)), + + _ => { + todo!("Invalid type") + } + } + } + PipelineData::ListStream(..) => { + todo!() + } + PipelineData::ByteStream(stream, ..) => { + let span = stream.span(); + Ok((stream.into_string()?, span)) + } + PipelineData::Empty => { + todo!("Can't have empty data"); + } + } +} + +pub fn get_binary(input: PipelineData, call_span: Span) -> Result<(Vec, Span), ShellError> { + match input { + PipelineData::Value(val, ..) => { + let span = val.span(); + match val { + Value::Binary { val, .. } => Ok((val, span)), + Value::String { val, .. } => Ok((val.into_bytes(), span)), + + _ => { + todo!("Invalid type") + } + } + } + PipelineData::ListStream(..) => { + todo!() + } + PipelineData::ByteStream(stream, ..) => { + let span = stream.span(); + Ok((stream.into_bytes()?, span)) + } + PipelineData::Empty => { + todo!("Can't have empty data"); + } + } +} diff --git a/crates/nu-command/src/strings/mod.rs b/crates/nu-command/src/strings/mod.rs index 8b5af2dec4..d92d37329d 100644 --- a/crates/nu-command/src/strings/mod.rs +++ b/crates/nu-command/src/strings/mod.rs @@ -1,3 +1,4 @@ +mod base; mod char_; mod detect_columns; mod encode_decode; @@ -7,6 +8,7 @@ mod parse; mod split; mod str_; +pub use base::{DecodeBase, EncodeBase}; pub use char_::Char; pub use detect_columns::*; pub use encode_decode::*; From 51b0f97fdacd24117d7b9a1e8f44bafa9ec06835 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Tue, 23 Jul 2024 19:32:07 +0300 Subject: [PATCH 02/19] Mid-refactor --- crates/nu-command/src/default_context.rs | 2 - crates/nu-command/src/strings/base/base32.rs | 62 +++++++++++++ .../src/strings/base/decode_base32.rs | 66 ++++++++++++++ .../src/strings/base/decode_base32hex.rs | 67 +++++++++++++++ .../base/{decode.rs => decode_base64.rs} | 0 .../nu-command/src/strings/base/decode_hex.rs | 76 ++++++++++++++++ .../src/strings/base/encode_base32.rs | 70 +++++++++++++++ .../base/{encode.rs => encode_base32hex.rs} | 0 .../src/strings/base/encode_base64.rs | 79 +++++++++++++++++ .../nu-command/src/strings/base/encode_hex.rs | 79 +++++++++++++++++ crates/nu-command/src/strings/base/mod.rs | 86 +++++++++++-------- 11 files changed, 547 insertions(+), 40 deletions(-) create mode 100644 crates/nu-command/src/strings/base/base32.rs create mode 100644 crates/nu-command/src/strings/base/decode_base32.rs create mode 100644 crates/nu-command/src/strings/base/decode_base32hex.rs rename crates/nu-command/src/strings/base/{decode.rs => decode_base64.rs} (100%) create mode 100644 crates/nu-command/src/strings/base/decode_hex.rs create mode 100644 crates/nu-command/src/strings/base/encode_base32.rs rename crates/nu-command/src/strings/base/{encode.rs => encode_base32hex.rs} (100%) create mode 100644 crates/nu-command/src/strings/base/encode_base64.rs create mode 100644 crates/nu-command/src/strings/base/encode_hex.rs diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 4cbce1003c..4adcfe9214 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -179,8 +179,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Char, Decode, Encode, - EncodeBase, - DecodeBase, DecodeBase64, EncodeBase64, DetectColumns, diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs new file mode 100644 index 0000000000..6053fd9e8c --- /dev/null +++ b/crates/nu-command/src/strings/base/base32.rs @@ -0,0 +1,62 @@ +use data_encoding::Encoding; + +use nu_engine::command_prelude::*; + +pub struct Base32Config { + lower: bool, + lower_span: Option, + nopad: bool, + nopad_span: Option, + dnscurve: bool, + dnscurve_span: Option, + dnssec: bool, + dnssec_span: Option, +} + +impl Base32Config { + pub fn new(engine_state: &EngineState, stack: &mut Stack, call: &Call) -> Self { + Base32Config { + lower: call.get_flag(engine_state, stack, "lower"), + lower_span: call.get_flag_span(stack, "lower"), + nopad: call.get_flag(engine_state, stack, "nopad"), + nopad_span: call.get_flag_span(stack, "nopad"), + dnscurve: call.get_flag(engine_state, stack, "dnscurve"), + dnscurve_span: call.get_flag_span(stack, "dnscurve"), + dnssec: call.get_flag(engine_state, stack, "dnssec"), + dnssec_span: call.get_flag_span(stack, "dnssec"), + } + } +} + +fn base32_encoding(config: Base32Config) -> Result { + if let Some(dnscurve_span) = config.dnscurve_span { + if let Some(lower_span) = config.lower_span { + return Err(ShellError::IncompatibleParameters { + left_message: "Inapplicable to DNSCURVE".to_string(), + left_span: lower_span, + right_message: "DNSCURVE must be used standalone".to_string(), + right_span: dnscurve_span, + }); + } + if let Some(nopad_span) = config.nopad_span { + return Err(ShellError::IncompatibleParameters { + left_message: "Inapplicable to DNSCURVE".to_string(), + left_span: nopad_span, + right_message: "DNSCURVE must be used standalone".to_string(), + right_span: dnscurve_span, + }); + } + if let Some(dnssec) = config.dnssec_span { + return Err(ShellError::IncompatibleParameters { + left_message: "Inapplicable to DNSCURVE".to_string(), + left_span: nopad_span, + right_message: "DNSCURVE must be used standalone".to_string(), + right_span: dnscurve_span, + }); + } + + return Ok(data_encoding::BASE32_DNSCURVE); + } + + todo!() +} diff --git a/crates/nu-command/src/strings/base/decode_base32.rs b/crates/nu-command/src/strings/base/decode_base32.rs new file mode 100644 index 0000000000..a9a9493946 --- /dev/null +++ b/crates/nu-command/src/strings/base/decode_base32.rs @@ -0,0 +1,66 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct DecodeBase32; + +impl Command for DecodeBase32 { + fn name(&self) -> &str { + "decode base32" + } + + fn signature(&self) -> Signature { + Signature::build("decode base32") + .input_output_types(vec![(Type::String, Type::Binary)]) + .allow_variants_without_examples(true) + .switch("lower", "Use a lowercase version of Base32.", None) + .switch("nopad", "Do not pad the output.", None) + .switch("dnscurve", "Use DNSCURVE Base32 variant.", None) + .switch("dnssec", "Use DNSSEC Base32 variant.", None) + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Decode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(DecodeBase32) + } +} diff --git a/crates/nu-command/src/strings/base/decode_base32hex.rs b/crates/nu-command/src/strings/base/decode_base32hex.rs new file mode 100644 index 0000000000..f0fec6d6be --- /dev/null +++ b/crates/nu-command/src/strings/base/decode_base32hex.rs @@ -0,0 +1,67 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct DecodeBase; + +impl Command for DecodeBase { + fn name(&self) -> &str { + "decode base" + } + + fn signature(&self) -> Signature { + Signature::build("decode base") + .input_output_types(vec![(Type::String, Type::Binary)]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Decode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req(engine_state, stack, 0)?; + + decode(&name, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req_const(working_set, 0)?; + + decode(&name, call.span(), input) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(DecodeBase) + } +} diff --git a/crates/nu-command/src/strings/base/decode.rs b/crates/nu-command/src/strings/base/decode_base64.rs similarity index 100% rename from crates/nu-command/src/strings/base/decode.rs rename to crates/nu-command/src/strings/base/decode_base64.rs diff --git a/crates/nu-command/src/strings/base/decode_hex.rs b/crates/nu-command/src/strings/base/decode_hex.rs new file mode 100644 index 0000000000..af494750a6 --- /dev/null +++ b/crates/nu-command/src/strings/base/decode_hex.rs @@ -0,0 +1,76 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct DecodeBase; + +impl Command for DecodeBase { + fn name(&self) -> &str { + "decode base" + } + + fn signature(&self) -> Signature { + Signature::build("decode base") + .input_output_types(vec![(Type::String, Type::Binary)]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Decode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req(engine_state, stack, 0)?; + + decode(&name, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req_const(working_set, 0)?; + + decode(&name, call.span(), input) + } +} + +fn decode(name: &str, call_span: Span, input: PipelineData) -> Result { + let encoding = super::encoding(&name, call_span, call_span)?; + let metadata = input.metadata(); + let (input_str, input_span) = super::get_string(input, call_span)?; + let output = encoding.decode(input_str.as_bytes()).unwrap(); + + Ok(Value::binary(output, call_span).into_pipeline_data_with_metadata(metadata)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(DecodeBase) + } +} diff --git a/crates/nu-command/src/strings/base/encode_base32.rs b/crates/nu-command/src/strings/base/encode_base32.rs new file mode 100644 index 0000000000..d2411bd8ab --- /dev/null +++ b/crates/nu-command/src/strings/base/encode_base32.rs @@ -0,0 +1,70 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct EncodeBase; + +impl Command for EncodeBase { + fn name(&self) -> &str { + "encode base" + } + + fn signature(&self) -> Signature { + Signature::build("encode base") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req(engine_state, stack, 0)?; + + encode(&name, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req_const(working_set, 0)?; + + encode(&name, call.span(), input) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(EncodeBase) + } +} diff --git a/crates/nu-command/src/strings/base/encode.rs b/crates/nu-command/src/strings/base/encode_base32hex.rs similarity index 100% rename from crates/nu-command/src/strings/base/encode.rs rename to crates/nu-command/src/strings/base/encode_base32hex.rs diff --git a/crates/nu-command/src/strings/base/encode_base64.rs b/crates/nu-command/src/strings/base/encode_base64.rs new file mode 100644 index 0000000000..970ce3c429 --- /dev/null +++ b/crates/nu-command/src/strings/base/encode_base64.rs @@ -0,0 +1,79 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct EncodeBase; + +impl Command for EncodeBase { + fn name(&self) -> &str { + "encode base" + } + + fn signature(&self) -> Signature { + Signature::build("encode base") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req(engine_state, stack, 0)?; + + encode(&name, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req_const(working_set, 0)?; + + encode(&name, call.span(), input) + } +} + +fn encode(name: &str, call_span: Span, input: PipelineData) -> Result { + let encoding = super::encoding(name, call_span, call_span)?; + let metadata = input.metadata(); + let (input_bytes, input_span) = super::get_binary(input, call_span)?; + let output = encoding.encode(&input_bytes); + + Ok(Value::string(output, call_span).into_pipeline_data_with_metadata(metadata)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(EncodeBase) + } +} diff --git a/crates/nu-command/src/strings/base/encode_hex.rs b/crates/nu-command/src/strings/base/encode_hex.rs new file mode 100644 index 0000000000..970ce3c429 --- /dev/null +++ b/crates/nu-command/src/strings/base/encode_hex.rs @@ -0,0 +1,79 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct EncodeBase; + +impl Command for EncodeBase { + fn name(&self) -> &str { + "encode base" + } + + fn signature(&self) -> Signature { + Signature::build("encode base") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req(engine_state, stack, 0)?; + + encode(&name, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let name: String = call.req_const(working_set, 0)?; + + encode(&name, call.span(), input) + } +} + +fn encode(name: &str, call_span: Span, input: PipelineData) -> Result { + let encoding = super::encoding(name, call_span, call_span)?; + let metadata = input.metadata(); + let (input_bytes, input_span) = super::get_binary(input, call_span)?; + let output = encoding.encode(&input_bytes); + + Ok(Value::string(output, call_span).into_pipeline_data_with_metadata(metadata)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + crate::test_examples(EncodeBase) + } +} diff --git a/crates/nu-command/src/strings/base/mod.rs b/crates/nu-command/src/strings/base/mod.rs index e14516eab9..44f5460de8 100644 --- a/crates/nu-command/src/strings/base/mod.rs +++ b/crates/nu-command/src/strings/base/mod.rs @@ -1,42 +1,52 @@ -use nu_protocol::{PipelineData, ShellError, Span, Value}; +use data_encoding::Encoding; -mod decode; -mod encode; +use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value}; -pub use decode::DecodeBase; -pub use encode::EncodeBase; +mod decode_base32; +mod decode_base32hex; +mod decode_base64; +mod decode_hex; +mod encode_base32; +mod encode_base32hex; +mod encode_base64; +mod encode_hex; -pub fn encoding( - name: &str, - val_span: Span, +mod base32; + +fn decode( + encoding: Encoding, call_span: Span, -) -> Result { - match name { - "base32" => Ok(data_encoding::BASE32), - "base32hex" => Ok(data_encoding::BASE32HEX), - "base32hex_nopad" => Ok(data_encoding::BASE32HEX_NOPAD), - "base32_dnscurve" => Ok(data_encoding::BASE32_DNSCURVE), - "base32_dnssec" => Ok(data_encoding::BASE32_DNSSEC), - "base32_nopad" => Ok(data_encoding::BASE32_NOPAD), - "base64" => Ok(data_encoding::BASE64), - "base64url" => Ok(data_encoding::BASE64URL), - "base64url_nopad" => Ok(data_encoding::BASE64URL_NOPAD), - "base64_mime" => Ok(data_encoding::BASE64_MIME), - "base64_mime_permissive" => Ok(data_encoding::BASE64_MIME_PERMISSIVE), - "base64_nopad" => Ok(data_encoding::BASE64_NOPAD), - "hexlower" => Ok(data_encoding::HEXLOWER), - "hexlower_permissive" => Ok(data_encoding::HEXLOWER_PERMISSIVE), - "hexupper" => Ok(data_encoding::HEXUPPER), - "hexupper_permissive" => Ok(data_encoding::HEXUPPER_PERMISSIVE), - _ => Err(ShellError::IncorrectValue { - msg: format!("Encoding '{name}' not found"), - val_span, - call_span, - }), - } + input: PipelineData, +) -> Result { + let metadata = input.metadata(); + let (input_str, input_span) = get_string(input, call_span)?; + let output = match encoding.decode(input_str.as_bytes()) { + Ok(output) => output, + Err(err) => { + return Err(ShellError::IncorrectValue { + msg: err.to_string(), + val_span: input_span, + call_span, + }); + } + }; + + Ok(Value::binary(output, call_span).into_pipeline_data_with_metadata(metadata)) } -pub fn get_string(input: PipelineData, call_span: Span) -> Result<(String, Span), ShellError> { +fn encode( + encoding: Encoding, + call_span: Span, + input: PipelineData, +) -> Result { + let metadata = input.metadata(); + let (input_bytes, _) = get_binary(input, call_span)?; + let output = encoding.encode(&input_bytes); + + Ok(Value::string(output, call_span).into_pipeline_data_with_metadata(metadata)) +} + +fn get_string(input: PipelineData, call_span: Span) -> Result<(String, Span), ShellError> { match input { PipelineData::Value(val, ..) => { let span = val.span(); @@ -55,19 +65,19 @@ pub fn get_string(input: PipelineData, call_span: Span) -> Result<(String, Span) let span = stream.span(); Ok((stream.into_string()?, span)) } - PipelineData::Empty => { - todo!("Can't have empty data"); - } + PipelineData::Empty => Err(ShellError::PipelineEmpty { + dst_span: call_span, + }), } } -pub fn get_binary(input: PipelineData, call_span: Span) -> Result<(Vec, Span), ShellError> { +fn get_binary(input: PipelineData, call_span: Span) -> Result<(Vec, Span), ShellError> { match input { PipelineData::Value(val, ..) => { let span = val.span(); match val { Value::Binary { val, .. } => Ok((val, span)), - Value::String { val, .. } => Ok((val.into_bytes(), span)), + Value::String { val, .. } => Ok((val.into_bytes(), span)), _ => { todo!("Invalid type") From 1f062c3b9008f0f2ceb07f30aa71b71955892ac7 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Tue, 23 Jul 2024 21:04:48 +0300 Subject: [PATCH 03/19] Complete subcommand splitting --- crates/nu-cmd-extra/src/extra/mod.rs | 2 - .../extra/strings/encode_decode/decode_hex.rs | 76 ------- .../extra/strings/encode_decode/encode_hex.rs | 63 ------ .../src/extra/strings/encode_decode/hex.rs | 192 ------------------ .../src/extra/strings/encode_decode/mod.rs | 6 - crates/nu-cmd-extra/src/extra/strings/mod.rs | 1 - crates/nu-command/src/default_context.rs | 7 + crates/nu-command/src/strings/base/base32.rs | 187 ++++++++++++++--- .../nu-command/src/strings/base/base32hex.rs | 149 ++++++++++++++ crates/nu-command/src/strings/base/base64.rs | 123 +++++++++++ .../src/strings/base/decode_base32.rs | 66 ------ .../src/strings/base/decode_base32hex.rs | 67 ------ .../src/strings/base/decode_base64.rs | 76 ------- .../nu-command/src/strings/base/decode_hex.rs | 76 ------- .../src/strings/base/encode_base32.rs | 70 ------- .../src/strings/base/encode_base32hex.rs | 79 ------- .../src/strings/base/encode_base64.rs | 79 ------- .../nu-command/src/strings/base/encode_hex.rs | 79 ------- crates/nu-command/src/strings/base/hex.rs | 134 ++++++++++++ crates/nu-command/src/strings/base/mod.rs | 24 +-- crates/nu-command/src/strings/mod.rs | 4 +- 21 files changed, 582 insertions(+), 978 deletions(-) delete mode 100644 crates/nu-cmd-extra/src/extra/strings/encode_decode/decode_hex.rs delete mode 100644 crates/nu-cmd-extra/src/extra/strings/encode_decode/encode_hex.rs delete mode 100644 crates/nu-cmd-extra/src/extra/strings/encode_decode/hex.rs delete mode 100644 crates/nu-cmd-extra/src/extra/strings/encode_decode/mod.rs create mode 100644 crates/nu-command/src/strings/base/base32hex.rs create mode 100644 crates/nu-command/src/strings/base/base64.rs delete mode 100644 crates/nu-command/src/strings/base/decode_base32.rs delete mode 100644 crates/nu-command/src/strings/base/decode_base32hex.rs delete mode 100644 crates/nu-command/src/strings/base/decode_base64.rs delete mode 100644 crates/nu-command/src/strings/base/decode_hex.rs delete mode 100644 crates/nu-command/src/strings/base/encode_base32.rs delete mode 100644 crates/nu-command/src/strings/base/encode_base32hex.rs delete mode 100644 crates/nu-command/src/strings/base/encode_base64.rs delete mode 100644 crates/nu-command/src/strings/base/encode_hex.rs create mode 100644 crates/nu-command/src/strings/base/hex.rs diff --git a/crates/nu-cmd-extra/src/extra/mod.rs b/crates/nu-cmd-extra/src/extra/mod.rs index 1877039a6f..3936b94a0f 100644 --- a/crates/nu-cmd-extra/src/extra/mod.rs +++ b/crates/nu-cmd-extra/src/extra/mod.rs @@ -45,8 +45,6 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState { bind_command!( strings::format::FormatPattern, - strings::encode_decode::EncodeHex, - strings::encode_decode::DecodeHex, strings::str_::case::Str, strings::str_::case::StrCamelCase, strings::str_::case::StrKebabCase, diff --git a/crates/nu-cmd-extra/src/extra/strings/encode_decode/decode_hex.rs b/crates/nu-cmd-extra/src/extra/strings/encode_decode/decode_hex.rs deleted file mode 100644 index 7be007d1c7..0000000000 --- a/crates/nu-cmd-extra/src/extra/strings/encode_decode/decode_hex.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::hex::{operate, ActionType}; -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct DecodeHex; - -impl Command for DecodeHex { - fn name(&self) -> &str { - "decode hex" - } - - fn signature(&self) -> Signature { - Signature::build("decode hex") - .input_output_types(vec![ - (Type::String, Type::Binary), - ( - Type::List(Box::new(Type::String)), - Type::List(Box::new(Type::Binary)), - ), - (Type::table(), Type::table()), - (Type::record(), Type::record()), - ]) - .allow_variants_without_examples(true) - .rest( - "rest", - SyntaxShape::CellPath, - "For a data structure input, decode data at the given cell paths", - ) - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Hex decode a value." - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Hex decode a value and output as binary", - example: "'0102030A0a0B' | decode hex", - result: Some(Value::binary( - [0x01, 0x02, 0x03, 0x0A, 0x0A, 0x0B], - Span::test_data(), - )), - }, - Example { - description: "Whitespaces are allowed to be between hex digits", - example: "'01 02 03 0A 0a 0B' | decode hex", - result: Some(Value::binary( - [0x01, 0x02, 0x03, 0x0A, 0x0A, 0x0B], - Span::test_data(), - )), - }, - ] - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - operate(ActionType::Decode, engine_state, stack, call, input) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(DecodeHex) - } -} diff --git a/crates/nu-cmd-extra/src/extra/strings/encode_decode/encode_hex.rs b/crates/nu-cmd-extra/src/extra/strings/encode_decode/encode_hex.rs deleted file mode 100644 index d261f7fe1f..0000000000 --- a/crates/nu-cmd-extra/src/extra/strings/encode_decode/encode_hex.rs +++ /dev/null @@ -1,63 +0,0 @@ -use super::hex::{operate, ActionType}; -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct EncodeHex; - -impl Command for EncodeHex { - fn name(&self) -> &str { - "encode hex" - } - - fn signature(&self) -> Signature { - Signature::build("encode hex") - .input_output_types(vec![ - (Type::Binary, Type::String), - ( - Type::List(Box::new(Type::Binary)), - Type::List(Box::new(Type::String)), - ), - (Type::table(), Type::table()), - (Type::record(), Type::record()), - ]) - .allow_variants_without_examples(true) - .rest( - "rest", - SyntaxShape::CellPath, - "For a data structure input, encode data at the given cell paths", - ) - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Encode a binary value using hex." - } - - fn examples(&self) -> Vec { - vec![Example { - description: "Encode binary data", - example: "0x[09 F9 11 02 9D 74 E3 5B D8 41 56 C5 63 56 88 C0] | encode hex", - result: Some(Value::test_string("09F911029D74E35BD84156C5635688C0")), - }] - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - operate(ActionType::Encode, engine_state, stack, call, input) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(EncodeHex) - } -} diff --git a/crates/nu-cmd-extra/src/extra/strings/encode_decode/hex.rs b/crates/nu-cmd-extra/src/extra/strings/encode_decode/hex.rs deleted file mode 100644 index be681b382a..0000000000 --- a/crates/nu-cmd-extra/src/extra/strings/encode_decode/hex.rs +++ /dev/null @@ -1,192 +0,0 @@ -use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument}; -use nu_engine::command_prelude::*; - -enum HexDecodingError { - InvalidLength(usize), - InvalidDigit(usize, char), -} - -fn hex_decode(value: &str) -> Result, HexDecodingError> { - let mut digits = value - .chars() - .enumerate() - .filter(|(_, c)| !c.is_whitespace()); - - let mut res = Vec::with_capacity(value.len() / 2); - loop { - let c1 = match digits.next() { - Some((ind, c)) => match c.to_digit(16) { - Some(d) => d, - None => return Err(HexDecodingError::InvalidDigit(ind, c)), - }, - None => return Ok(res), - }; - let c2 = match digits.next() { - Some((ind, c)) => match c.to_digit(16) { - Some(d) => d, - None => return Err(HexDecodingError::InvalidDigit(ind, c)), - }, - None => { - return Err(HexDecodingError::InvalidLength(value.len())); - } - }; - res.push((c1 << 4 | c2) as u8); - } -} - -fn hex_digit(num: u8) -> char { - match num { - 0..=9 => (num + b'0') as char, - 10..=15 => (num - 10 + b'A') as char, - _ => unreachable!(), - } -} - -fn hex_encode(bytes: &[u8]) -> String { - let mut res = String::with_capacity(bytes.len() * 2); - for byte in bytes { - res.push(hex_digit(byte >> 4)); - res.push(hex_digit(byte & 0b1111)); - } - res -} - -#[derive(Clone)] -pub struct HexConfig { - pub action_type: ActionType, -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum ActionType { - Encode, - Decode, -} - -struct Arguments { - cell_paths: Option>, - encoding_config: HexConfig, -} - -impl CmdArgument for Arguments { - fn take_cell_paths(&mut self) -> Option> { - self.cell_paths.take() - } -} - -pub fn operate( - action_type: ActionType, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, -) -> Result { - let cell_paths: Vec = call.rest(engine_state, stack, 0)?; - let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths); - - let args = Arguments { - encoding_config: HexConfig { action_type }, - cell_paths, - }; - - general_operate(action, args, input, call.head, engine_state.signals()) -} - -fn action( - input: &Value, - // only used for `decode` action - args: &Arguments, - command_span: Span, -) -> Value { - let hex_config = &args.encoding_config; - - match input { - // Propagate existing errors. - Value::Error { .. } => input.clone(), - Value::Binary { val, .. } => match hex_config.action_type { - ActionType::Encode => Value::string(hex_encode(val.as_ref()), command_span), - ActionType::Decode => Value::error( - ShellError::UnsupportedInput { msg: "Binary data can only be encoded".to_string(), input: "value originates from here".into(), msg_span: command_span, input_span: input.span() }, - command_span, - ), - }, - Value::String { val, .. } => { - match hex_config.action_type { - ActionType::Encode => Value::error( - ShellError::UnsupportedInput { msg: "String value can only be decoded".to_string(), input: "value originates from here".into(), msg_span: command_span, input_span: input.span() }, - command_span, - ), - - ActionType::Decode => match hex_decode(val.as_ref()) { - Ok(decoded_value) => Value::binary(decoded_value, command_span), - Err(HexDecodingError::InvalidLength(len)) => Value::error(ShellError::GenericError { - error: "value could not be hex decoded".into(), - msg: format!("invalid hex input length: {len}. The length should be even"), - span: Some(command_span), - help: None, - inner: vec![], - }, - command_span, - ), - Err(HexDecodingError::InvalidDigit(index, digit)) => Value::error(ShellError::GenericError { - error: "value could not be hex decoded".into(), - msg: format!("invalid hex digit: '{digit}' at index {index}. Only 0-9, A-F, a-f are allowed in hex encoding"), - span: Some(command_span), - help: None, - inner: vec![], - }, - command_span, - ), - }, - } - } - other => Value::error( - ShellError::TypeMismatch { - err_message: format!("string or binary, not {}", other.get_type()), - span: other.span(), - }, - other.span(), - ), - } -} - -#[cfg(test)] -mod tests { - use super::{action, ActionType, Arguments, HexConfig}; - use nu_protocol::{Span, Value}; - - #[test] - fn hex_encode() { - let word = Value::binary([77, 97, 110], Span::test_data()); - let expected = Value::test_string("4D616E"); - - let actual = action( - &word, - &Arguments { - encoding_config: HexConfig { - action_type: ActionType::Encode, - }, - cell_paths: None, - }, - Span::test_data(), - ); - assert_eq!(actual, expected); - } - - #[test] - fn hex_decode() { - let word = Value::test_string("4D 61\r\n\n6E"); - let expected = Value::binary([77, 97, 110], Span::test_data()); - - let actual = action( - &word, - &Arguments { - encoding_config: HexConfig { - action_type: ActionType::Decode, - }, - cell_paths: None, - }, - Span::test_data(), - ); - assert_eq!(actual, expected); - } -} diff --git a/crates/nu-cmd-extra/src/extra/strings/encode_decode/mod.rs b/crates/nu-cmd-extra/src/extra/strings/encode_decode/mod.rs deleted file mode 100644 index 581b87679f..0000000000 --- a/crates/nu-cmd-extra/src/extra/strings/encode_decode/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod decode_hex; -mod encode_hex; -mod hex; - -pub(crate) use decode_hex::DecodeHex; -pub(crate) use encode_hex::EncodeHex; diff --git a/crates/nu-cmd-extra/src/extra/strings/mod.rs b/crates/nu-cmd-extra/src/extra/strings/mod.rs index 391cf108e0..8d43cd8a65 100644 --- a/crates/nu-cmd-extra/src/extra/strings/mod.rs +++ b/crates/nu-cmd-extra/src/extra/strings/mod.rs @@ -1,3 +1,2 @@ -pub(crate) mod encode_decode; pub(crate) mod format; pub(crate) mod str_; diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 4adcfe9214..c625dcd913 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -176,6 +176,13 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { // Strings bind_command! { + DecodeBase32, + EncodeBase32, + DecodeBase32Hex, + EncodeBase32Hex, + DecodeHex, + EncodeHex, + Char, Decode, Encode, diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs index 6053fd9e8c..854bcfd3a6 100644 --- a/crates/nu-command/src/strings/base/base32.rs +++ b/crates/nu-command/src/strings/base/base32.rs @@ -3,60 +3,181 @@ use data_encoding::Encoding; use nu_engine::command_prelude::*; pub struct Base32Config { - lower: bool, - lower_span: Option, - nopad: bool, - nopad_span: Option, - dnscurve: bool, - dnscurve_span: Option, - dnssec: bool, - dnssec_span: Option, + nopad: Option, + dnscurve: Option, + dnssec: Option, } impl Base32Config { - pub fn new(engine_state: &EngineState, stack: &mut Stack, call: &Call) -> Self { + pub fn from(stack: &mut Stack, call: &Call) -> Self { Base32Config { - lower: call.get_flag(engine_state, stack, "lower"), - lower_span: call.get_flag_span(stack, "lower"), - nopad: call.get_flag(engine_state, stack, "nopad"), - nopad_span: call.get_flag_span(stack, "nopad"), - dnscurve: call.get_flag(engine_state, stack, "dnscurve"), - dnscurve_span: call.get_flag_span(stack, "dnscurve"), - dnssec: call.get_flag(engine_state, stack, "dnssec"), - dnssec_span: call.get_flag_span(stack, "dnssec"), + nopad: call.get_flag_span(stack, "nopad"), + dnscurve: call.get_flag_span(stack, "dnscurve"), + dnssec: call.get_flag_span(stack, "dnssec"), } } } -fn base32_encoding(config: Base32Config) -> Result { - if let Some(dnscurve_span) = config.dnscurve_span { - if let Some(lower_span) = config.lower_span { +pub fn base32_encoding(config: Base32Config) -> Result { + match (config.nopad, config.dnscurve, config.dnssec) { + (Some(nopad), Some(dnscurve), _) => { return Err(ShellError::IncompatibleParameters { left_message: "Inapplicable to DNSCURVE".to_string(), - left_span: lower_span, - right_message: "DNSCURVE must be used standalone".to_string(), - right_span: dnscurve_span, + left_span: nopad, + right_message: "Must be used standalone".to_string(), + right_span: dnscurve, }); } - if let Some(nopad_span) = config.nopad_span { + (_, Some(dnscurve), Some(dnssec)) => { return Err(ShellError::IncompatibleParameters { - left_message: "Inapplicable to DNSCURVE".to_string(), - left_span: nopad_span, - right_message: "DNSCURVE must be used standalone".to_string(), - right_span: dnscurve_span, + left_message: "Incompatible with DNSCURVE".to_string(), + left_span: dnssec, + right_message: "Must be used standalone".to_string(), + right_span: dnscurve, }); } - if let Some(dnssec) = config.dnssec_span { + (Some(nopad), _, Some(dnssec)) => { return Err(ShellError::IncompatibleParameters { left_message: "Inapplicable to DNSCURVE".to_string(), - left_span: nopad_span, + left_span: nopad, right_message: "DNSCURVE must be used standalone".to_string(), - right_span: dnscurve_span, + right_span: dnssec, }); } - return Ok(data_encoding::BASE32_DNSCURVE); + (None, None, None) => Ok(data_encoding::BASE32), + (Some(_), None, None) => Ok(data_encoding::BASE32_NOPAD), + (None, Some(_), None) => Ok(data_encoding::BASE32_DNSCURVE), + (None, None, Some(_)) => Ok(data_encoding::BASE32_DNSSEC), + } +} + +#[derive(Clone)] +pub struct DecodeBase32; + +impl Command for DecodeBase32 { + fn name(&self) -> &str { + "decode base32" } - todo!() + fn signature(&self) -> Signature { + Signature::build("decode base32") + .input_output_types(vec![(Type::String, Type::Binary)]) + .allow_variants_without_examples(true) + .switch("nopad", "Do not pad the output.", None) + .switch("dnscurve", "Use DNSCURVE Base32 variant.", None) + .switch("dnssec", "Use DNSSEC Base32 variant.", None) + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Decode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let config = Base32Config::from(stack, call); + let encoding = base32_encoding(config)?; + super::decode(encoding, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } +} + +#[derive(Clone)] +pub struct EncodeBase32; + +impl Command for EncodeBase32 { + fn name(&self) -> &str { + "encode base32" + } + + fn signature(&self) -> Signature { + Signature::build("encode base32") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .switch("nopad", "Don't accept padding.", None) + .switch("dnscurve", "Parse as the DNSCURVE Base32 variant.", None) + .switch("dnssec", "Parse as the DNSSEC Base32 variant.", None) + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let config = Base32Config::from(stack, call); + let encoding = base32_encoding(config)?; + super::encode(encoding, call.span(), input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples_decode() { + crate::test_examples(DecodeBase32) + } + + #[test] + fn test_examples_encode() { + crate::test_examples(EncodeBase32) + } } diff --git a/crates/nu-command/src/strings/base/base32hex.rs b/crates/nu-command/src/strings/base/base32hex.rs new file mode 100644 index 0000000000..a50e1753a2 --- /dev/null +++ b/crates/nu-command/src/strings/base/base32hex.rs @@ -0,0 +1,149 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct DecodeBase32Hex; + +impl Command for DecodeBase32Hex { + fn name(&self) -> &str { + "decode base32hex" + } + + fn signature(&self) -> Signature { + Signature::build("decode base32hex") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .switch("nopad", "Reject input with padding.", None) + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let encoding = if call.has_flag(engine_state, stack, "nopad")? { + data_encoding::BASE32HEX_NOPAD + } else { + data_encoding::BASE32HEX + }; + + super::decode(encoding, call.head, input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let encoding = if call.has_flag_const(working_set, "nopad")? { + data_encoding::BASE32HEX_NOPAD + } else { + data_encoding::BASE32HEX + }; + + super::decode(encoding, call.head, input) + } +} + +#[derive(Clone)] +pub struct EncodeBase32Hex; + +impl Command for EncodeBase32Hex { + fn name(&self) -> &str { + "encode base32hex" + } + + fn signature(&self) -> Signature { + Signature::build("encode base32hex") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .switch("nopad", "Don't pad the output.", None) + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let encoding = if call.has_flag(engine_state, stack, "nopad")? { + data_encoding::BASE32HEX_NOPAD + } else { + data_encoding::BASE32HEX + }; + + super::encode(encoding, call.head, input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let encoding = if call.has_flag_const(working_set, "nopad")? { + data_encoding::BASE32HEX_NOPAD + } else { + data_encoding::BASE32HEX + }; + + super::encode(encoding, call.head, input) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples_decode() { + crate::test_examples(DecodeBase32Hex) + } + #[test] + fn test_examples_encode() { + crate::test_examples(EncodeBase32Hex) + } +} diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs new file mode 100644 index 0000000000..f6eb522895 --- /dev/null +++ b/crates/nu-command/src/strings/base/base64.rs @@ -0,0 +1,123 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct DecodeBase64; + +impl Command for DecodeBase64 { + fn name(&self) -> &str { + "decode base64" + } + + fn signature(&self) -> Signature { + Signature::build("decode base64") + .input_output_types(vec![(Type::String, Type::Binary)]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Decode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } +} + +#[derive(Clone)] +pub struct EncodeBase64; + +impl Command for EncodeBase64 { + fn name(&self) -> &str { + "encode base64" + } + + fn signature(&self) -> Signature { + Signature::build("encode base64") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .required("encoding", SyntaxShape::String, "encoding to use") + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "Encode a value." + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples_decode() { + crate::test_examples(DecodeBase64) + } + + #[test] + fn test_examples_encode() { + crate::test_examples(EncodeBase64) + } +} diff --git a/crates/nu-command/src/strings/base/decode_base32.rs b/crates/nu-command/src/strings/base/decode_base32.rs deleted file mode 100644 index a9a9493946..0000000000 --- a/crates/nu-command/src/strings/base/decode_base32.rs +++ /dev/null @@ -1,66 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct DecodeBase32; - -impl Command for DecodeBase32 { - fn name(&self) -> &str { - "decode base32" - } - - fn signature(&self) -> Signature { - Signature::build("decode base32") - .input_output_types(vec![(Type::String, Type::Binary)]) - .allow_variants_without_examples(true) - .switch("lower", "Use a lowercase version of Base32.", None) - .switch("nopad", "Do not pad the output.", None) - .switch("dnscurve", "Use DNSCURVE Base32 variant.", None) - .switch("dnssec", "Use DNSSEC Base32 variant.", None) - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Decode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - todo!() - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - todo!() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(DecodeBase32) - } -} diff --git a/crates/nu-command/src/strings/base/decode_base32hex.rs b/crates/nu-command/src/strings/base/decode_base32hex.rs deleted file mode 100644 index f0fec6d6be..0000000000 --- a/crates/nu-command/src/strings/base/decode_base32hex.rs +++ /dev/null @@ -1,67 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct DecodeBase; - -impl Command for DecodeBase { - fn name(&self) -> &str { - "decode base" - } - - fn signature(&self) -> Signature { - Signature::build("decode base") - .input_output_types(vec![(Type::String, Type::Binary)]) - .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Decode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req(engine_state, stack, 0)?; - - decode(&name, call.span(), input) - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req_const(working_set, 0)?; - - decode(&name, call.span(), input) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(DecodeBase) - } -} diff --git a/crates/nu-command/src/strings/base/decode_base64.rs b/crates/nu-command/src/strings/base/decode_base64.rs deleted file mode 100644 index af494750a6..0000000000 --- a/crates/nu-command/src/strings/base/decode_base64.rs +++ /dev/null @@ -1,76 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct DecodeBase; - -impl Command for DecodeBase { - fn name(&self) -> &str { - "decode base" - } - - fn signature(&self) -> Signature { - Signature::build("decode base") - .input_output_types(vec![(Type::String, Type::Binary)]) - .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Decode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req(engine_state, stack, 0)?; - - decode(&name, call.span(), input) - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req_const(working_set, 0)?; - - decode(&name, call.span(), input) - } -} - -fn decode(name: &str, call_span: Span, input: PipelineData) -> Result { - let encoding = super::encoding(&name, call_span, call_span)?; - let metadata = input.metadata(); - let (input_str, input_span) = super::get_string(input, call_span)?; - let output = encoding.decode(input_str.as_bytes()).unwrap(); - - Ok(Value::binary(output, call_span).into_pipeline_data_with_metadata(metadata)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(DecodeBase) - } -} diff --git a/crates/nu-command/src/strings/base/decode_hex.rs b/crates/nu-command/src/strings/base/decode_hex.rs deleted file mode 100644 index af494750a6..0000000000 --- a/crates/nu-command/src/strings/base/decode_hex.rs +++ /dev/null @@ -1,76 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct DecodeBase; - -impl Command for DecodeBase { - fn name(&self) -> &str { - "decode base" - } - - fn signature(&self) -> Signature { - Signature::build("decode base") - .input_output_types(vec![(Type::String, Type::Binary)]) - .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Decode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req(engine_state, stack, 0)?; - - decode(&name, call.span(), input) - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req_const(working_set, 0)?; - - decode(&name, call.span(), input) - } -} - -fn decode(name: &str, call_span: Span, input: PipelineData) -> Result { - let encoding = super::encoding(&name, call_span, call_span)?; - let metadata = input.metadata(); - let (input_str, input_span) = super::get_string(input, call_span)?; - let output = encoding.decode(input_str.as_bytes()).unwrap(); - - Ok(Value::binary(output, call_span).into_pipeline_data_with_metadata(metadata)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(DecodeBase) - } -} diff --git a/crates/nu-command/src/strings/base/encode_base32.rs b/crates/nu-command/src/strings/base/encode_base32.rs deleted file mode 100644 index d2411bd8ab..0000000000 --- a/crates/nu-command/src/strings/base/encode_base32.rs +++ /dev/null @@ -1,70 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct EncodeBase; - -impl Command for EncodeBase { - fn name(&self) -> &str { - "encode base" - } - - fn signature(&self) -> Signature { - Signature::build("encode base") - .input_output_types(vec![ - (Type::String, Type::String), - (Type::Binary, Type::String), - ]) - .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Encode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req(engine_state, stack, 0)?; - - encode(&name, call.span(), input) - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req_const(working_set, 0)?; - - encode(&name, call.span(), input) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(EncodeBase) - } -} diff --git a/crates/nu-command/src/strings/base/encode_base32hex.rs b/crates/nu-command/src/strings/base/encode_base32hex.rs deleted file mode 100644 index 970ce3c429..0000000000 --- a/crates/nu-command/src/strings/base/encode_base32hex.rs +++ /dev/null @@ -1,79 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct EncodeBase; - -impl Command for EncodeBase { - fn name(&self) -> &str { - "encode base" - } - - fn signature(&self) -> Signature { - Signature::build("encode base") - .input_output_types(vec![ - (Type::String, Type::String), - (Type::Binary, Type::String), - ]) - .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Encode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req(engine_state, stack, 0)?; - - encode(&name, call.span(), input) - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req_const(working_set, 0)?; - - encode(&name, call.span(), input) - } -} - -fn encode(name: &str, call_span: Span, input: PipelineData) -> Result { - let encoding = super::encoding(name, call_span, call_span)?; - let metadata = input.metadata(); - let (input_bytes, input_span) = super::get_binary(input, call_span)?; - let output = encoding.encode(&input_bytes); - - Ok(Value::string(output, call_span).into_pipeline_data_with_metadata(metadata)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(EncodeBase) - } -} diff --git a/crates/nu-command/src/strings/base/encode_base64.rs b/crates/nu-command/src/strings/base/encode_base64.rs deleted file mode 100644 index 970ce3c429..0000000000 --- a/crates/nu-command/src/strings/base/encode_base64.rs +++ /dev/null @@ -1,79 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct EncodeBase; - -impl Command for EncodeBase { - fn name(&self) -> &str { - "encode base" - } - - fn signature(&self) -> Signature { - Signature::build("encode base") - .input_output_types(vec![ - (Type::String, Type::String), - (Type::Binary, Type::String), - ]) - .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Encode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req(engine_state, stack, 0)?; - - encode(&name, call.span(), input) - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req_const(working_set, 0)?; - - encode(&name, call.span(), input) - } -} - -fn encode(name: &str, call_span: Span, input: PipelineData) -> Result { - let encoding = super::encoding(name, call_span, call_span)?; - let metadata = input.metadata(); - let (input_bytes, input_span) = super::get_binary(input, call_span)?; - let output = encoding.encode(&input_bytes); - - Ok(Value::string(output, call_span).into_pipeline_data_with_metadata(metadata)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(EncodeBase) - } -} diff --git a/crates/nu-command/src/strings/base/encode_hex.rs b/crates/nu-command/src/strings/base/encode_hex.rs deleted file mode 100644 index 970ce3c429..0000000000 --- a/crates/nu-command/src/strings/base/encode_hex.rs +++ /dev/null @@ -1,79 +0,0 @@ -use nu_engine::command_prelude::*; - -#[derive(Clone)] -pub struct EncodeBase; - -impl Command for EncodeBase { - fn name(&self) -> &str { - "encode base" - } - - fn signature(&self) -> Signature { - Signature::build("encode base") - .input_output_types(vec![ - (Type::String, Type::String), - (Type::Binary, Type::String), - ]) - .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") - .category(Category::Formats) - } - - fn usage(&self) -> &str { - "Encode a value." - } - - fn extra_usage(&self) -> &str { - "TODO" - } - - fn examples(&self) -> Vec { - vec![] - } - - fn is_const(&self) -> bool { - true - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req(engine_state, stack, 0)?; - - encode(&name, call.span(), input) - } - - fn run_const( - &self, - working_set: &StateWorkingSet, - call: &Call, - input: PipelineData, - ) -> Result { - let name: String = call.req_const(working_set, 0)?; - - encode(&name, call.span(), input) - } -} - -fn encode(name: &str, call_span: Span, input: PipelineData) -> Result { - let encoding = super::encoding(name, call_span, call_span)?; - let metadata = input.metadata(); - let (input_bytes, input_span) = super::get_binary(input, call_span)?; - let output = encoding.encode(&input_bytes); - - Ok(Value::string(output, call_span).into_pipeline_data_with_metadata(metadata)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_examples() { - crate::test_examples(EncodeBase) - } -} diff --git a/crates/nu-command/src/strings/base/hex.rs b/crates/nu-command/src/strings/base/hex.rs new file mode 100644 index 0000000000..2493b61c28 --- /dev/null +++ b/crates/nu-command/src/strings/base/hex.rs @@ -0,0 +1,134 @@ +use nu_engine::command_prelude::*; + +#[derive(Clone)] +pub struct DecodeHex; + +impl Command for DecodeHex { + fn name(&self) -> &str { + "decode base" + } + + fn signature(&self) -> Signature { + Signature::build("decode base") + .input_output_types(vec![(Type::String, Type::Binary)]) + .allow_variants_without_examples(true) + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "TODO" + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + super::decode(data_encoding::HEXLOWER_PERMISSIVE, call.head, input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + todo!() + } +} + +#[derive(Clone)] +pub struct EncodeHex; + +impl Command for EncodeHex { + fn name(&self) -> &str { + "encode hex" + } + + fn signature(&self) -> Signature { + Signature::build("encode hex") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Binary, Type::String), + ]) + .allow_variants_without_examples(true) + .switch("lower", "Encode to lowercase hex.", None) + .category(Category::Formats) + } + + fn usage(&self) -> &str { + "TODO" + } + + fn extra_usage(&self) -> &str { + "TODO" + } + + fn examples(&self) -> Vec { + vec![] + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let encoding = if call.has_flag(engine_state, stack, "lower")? { + data_encoding::HEXLOWER + } else { + data_encoding::HEXUPPER + }; + + super::encode(encoding, call.head, input) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + input: PipelineData, + ) -> Result { + let encoding = if call.has_flag_const(working_set, "lower")? { + data_encoding::HEXLOWER + } else { + data_encoding::HEXUPPER + }; + + super::encode(encoding, call.head, input) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples_decode() { + crate::test_examples(DecodeHex) + } + + #[test] + fn test_examples_encode() { + crate::test_examples(EncodeHex) + } +} diff --git a/crates/nu-command/src/strings/base/mod.rs b/crates/nu-command/src/strings/base/mod.rs index 44f5460de8..73b177f2b9 100644 --- a/crates/nu-command/src/strings/base/mod.rs +++ b/crates/nu-command/src/strings/base/mod.rs @@ -1,19 +1,19 @@ +#![allow(unused)] + use data_encoding::Encoding; -use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value}; - -mod decode_base32; -mod decode_base32hex; -mod decode_base64; -mod decode_hex; -mod encode_base32; -mod encode_base32hex; -mod encode_base64; -mod encode_hex; +use nu_engine::command_prelude::*; mod base32; +mod base32hex; +mod base64; +mod hex; -fn decode( +pub use base32::{DecodeBase32, EncodeBase32}; +pub use base32hex::{DecodeBase32Hex, EncodeBase32Hex}; +pub use hex::{DecodeHex, EncodeHex}; + +pub fn decode( encoding: Encoding, call_span: Span, input: PipelineData, @@ -34,7 +34,7 @@ fn decode( Ok(Value::binary(output, call_span).into_pipeline_data_with_metadata(metadata)) } -fn encode( +pub fn encode( encoding: Encoding, call_span: Span, input: PipelineData, diff --git a/crates/nu-command/src/strings/mod.rs b/crates/nu-command/src/strings/mod.rs index d92d37329d..deaea34146 100644 --- a/crates/nu-command/src/strings/mod.rs +++ b/crates/nu-command/src/strings/mod.rs @@ -8,7 +8,9 @@ mod parse; mod split; mod str_; -pub use base::{DecodeBase, EncodeBase}; +pub use base::{ + DecodeBase32, DecodeBase32Hex, DecodeHex, EncodeBase32, EncodeBase32Hex, EncodeHex, +}; pub use char_::Char; pub use detect_columns::*; pub use encode_decode::*; From 7a5365d7f4b90de94b78ee40b717ebdda90e2ed7 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Tue, 23 Jul 2024 21:23:20 +0300 Subject: [PATCH 04/19] Finish Base64 --- crates/nu-command/src/strings/base/base64.rs | 48 +++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index f6eb522895..53321fd48c 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -1,5 +1,34 @@ +use data_encoding::Encoding; + use nu_engine::command_prelude::*; +fn get_encoding_from_flags(url: bool, nopad: bool) -> Encoding { + match (url, nopad) { + (false, false) => data_encoding::BASE64, + (false, true) => data_encoding::BASE64_NOPAD, + (true, false) => data_encoding::BASE64URL, + (true, true) => data_encoding::BASE64URL_NOPAD, + } +} + +fn get_encoding( + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, +) -> Result { + let url = call.has_flag(engine_state, stack, "url")?; + let nopad = call.has_flag(engine_state, stack, "nopad")?; + + Ok(get_encoding_from_flags(url, nopad)) +} + +fn get_encoding_const(working_set: &StateWorkingSet, call: &Call) -> Result { + let url = call.has_flag_const(working_set, "url")?; + let nopad = call.has_flag_const(working_set, "nopad")?; + + Ok(get_encoding_from_flags(url, nopad)) +} + #[derive(Clone)] pub struct DecodeBase64; @@ -12,12 +41,13 @@ impl Command for DecodeBase64 { Signature::build("decode base64") .input_output_types(vec![(Type::String, Type::Binary)]) .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") + .switch("url", "Decode the URL-safe Base64 version.", None) + .switch("nopad", "Reject padding.", None) .category(Category::Formats) } fn usage(&self) -> &str { - "Decode a value." + "TODO" } fn extra_usage(&self) -> &str { @@ -39,7 +69,8 @@ impl Command for DecodeBase64 { call: &Call, input: PipelineData, ) -> Result { - todo!() + let encoding = get_encoding(engine_state, stack, call)?; + super::decode(encoding, call.head, input) } fn run_const( @@ -48,7 +79,8 @@ impl Command for DecodeBase64 { call: &Call, input: PipelineData, ) -> Result { - todo!() + let encoding = get_encoding_const(working_set, call)?; + super::decode(encoding, call.head, input) } } @@ -72,7 +104,7 @@ impl Command for EncodeBase64 { } fn usage(&self) -> &str { - "Encode a value." + "TODO" } fn extra_usage(&self) -> &str { @@ -94,7 +126,8 @@ impl Command for EncodeBase64 { call: &Call, input: PipelineData, ) -> Result { - todo!() + let encoding = get_encoding(engine_state, stack, call)?; + super::encode(encoding, call.head, input) } fn run_const( @@ -103,7 +136,8 @@ impl Command for EncodeBase64 { call: &Call, input: PipelineData, ) -> Result { - todo!() + let encoding = get_encoding_const(working_set, call)?; + super::encode(encoding, call.head, input) } } From 5af95c91afb4d69c5abfaf972c85aa8dc981ba42 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Tue, 23 Jul 2024 21:34:40 +0300 Subject: [PATCH 05/19] Lint --- crates/nu-command/src/strings/base/base32.rs | 42 +++++++++----------- crates/nu-command/src/strings/base/base64.rs | 3 +- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs index 854bcfd3a6..ad3fdd6cbc 100644 --- a/crates/nu-command/src/strings/base/base32.rs +++ b/crates/nu-command/src/strings/base/base32.rs @@ -20,30 +20,24 @@ impl Base32Config { pub fn base32_encoding(config: Base32Config) -> Result { match (config.nopad, config.dnscurve, config.dnssec) { - (Some(nopad), Some(dnscurve), _) => { - return Err(ShellError::IncompatibleParameters { - left_message: "Inapplicable to DNSCURVE".to_string(), - left_span: nopad, - right_message: "Must be used standalone".to_string(), - right_span: dnscurve, - }); - } - (_, Some(dnscurve), Some(dnssec)) => { - return Err(ShellError::IncompatibleParameters { - left_message: "Incompatible with DNSCURVE".to_string(), - left_span: dnssec, - right_message: "Must be used standalone".to_string(), - right_span: dnscurve, - }); - } - (Some(nopad), _, Some(dnssec)) => { - return Err(ShellError::IncompatibleParameters { - left_message: "Inapplicable to DNSCURVE".to_string(), - left_span: nopad, - right_message: "DNSCURVE must be used standalone".to_string(), - right_span: dnssec, - }); - } + (Some(nopad), Some(dnscurve), _) => Err(ShellError::IncompatibleParameters { + left_message: "Inapplicable to DNSCURVE".to_string(), + left_span: nopad, + right_message: "Must be used standalone".to_string(), + right_span: dnscurve, + }), + (_, Some(dnscurve), Some(dnssec)) => Err(ShellError::IncompatibleParameters { + left_message: "Incompatible with DNSCURVE".to_string(), + left_span: dnssec, + right_message: "Must be used standalone".to_string(), + right_span: dnscurve, + }), + (Some(nopad), _, Some(dnssec)) => Err(ShellError::IncompatibleParameters { + left_message: "Inapplicable to DNSCURVE".to_string(), + left_span: nopad, + right_message: "DNSCURVE must be used standalone".to_string(), + right_span: dnssec, + }), (None, None, None) => Ok(data_encoding::BASE32), (Some(_), None, None) => Ok(data_encoding::BASE32_NOPAD), diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index 53321fd48c..d0514f8f62 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -99,7 +99,8 @@ impl Command for EncodeBase64 { (Type::Binary, Type::String), ]) .allow_variants_without_examples(true) - .required("encoding", SyntaxShape::String, "encoding to use") + .switch("url", "Use the URL-safe Base64 version.", None) + .switch("nopad", "Don't pad the output.", None) .category(Category::Formats) } From f7bc29d108c5edc93a95233be69907aa01f0fa73 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Wed, 24 Jul 2024 11:55:11 +0300 Subject: [PATCH 06/19] Remove DNS options from Base32 --- crates/nu-command/src/strings/base/base32.rs | 74 ++++++-------------- 1 file changed, 22 insertions(+), 52 deletions(-) diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs index ad3fdd6cbc..c3f392e7a3 100644 --- a/crates/nu-command/src/strings/base/base32.rs +++ b/crates/nu-command/src/strings/base/base32.rs @@ -2,50 +2,6 @@ use data_encoding::Encoding; use nu_engine::command_prelude::*; -pub struct Base32Config { - nopad: Option, - dnscurve: Option, - dnssec: Option, -} - -impl Base32Config { - pub fn from(stack: &mut Stack, call: &Call) -> Self { - Base32Config { - nopad: call.get_flag_span(stack, "nopad"), - dnscurve: call.get_flag_span(stack, "dnscurve"), - dnssec: call.get_flag_span(stack, "dnssec"), - } - } -} - -pub fn base32_encoding(config: Base32Config) -> Result { - match (config.nopad, config.dnscurve, config.dnssec) { - (Some(nopad), Some(dnscurve), _) => Err(ShellError::IncompatibleParameters { - left_message: "Inapplicable to DNSCURVE".to_string(), - left_span: nopad, - right_message: "Must be used standalone".to_string(), - right_span: dnscurve, - }), - (_, Some(dnscurve), Some(dnssec)) => Err(ShellError::IncompatibleParameters { - left_message: "Incompatible with DNSCURVE".to_string(), - left_span: dnssec, - right_message: "Must be used standalone".to_string(), - right_span: dnscurve, - }), - (Some(nopad), _, Some(dnssec)) => Err(ShellError::IncompatibleParameters { - left_message: "Inapplicable to DNSCURVE".to_string(), - left_span: nopad, - right_message: "DNSCURVE must be used standalone".to_string(), - right_span: dnssec, - }), - - (None, None, None) => Ok(data_encoding::BASE32), - (Some(_), None, None) => Ok(data_encoding::BASE32_NOPAD), - (None, Some(_), None) => Ok(data_encoding::BASE32_DNSCURVE), - (None, None, Some(_)) => Ok(data_encoding::BASE32_DNSSEC), - } -} - #[derive(Clone)] pub struct DecodeBase32; @@ -59,8 +15,6 @@ impl Command for DecodeBase32 { .input_output_types(vec![(Type::String, Type::Binary)]) .allow_variants_without_examples(true) .switch("nopad", "Do not pad the output.", None) - .switch("dnscurve", "Use DNSCURVE Base32 variant.", None) - .switch("dnssec", "Use DNSSEC Base32 variant.", None) .category(Category::Formats) } @@ -87,8 +41,11 @@ impl Command for DecodeBase32 { call: &Call, input: PipelineData, ) -> Result { - let config = Base32Config::from(stack, call); - let encoding = base32_encoding(config)?; + let encoding = if call.has_flag(engine_state, stack, "nopad")? { + data_encoding::BASE32_NOPAD + } else { + data_encoding::BASE32 + }; super::decode(encoding, call.span(), input) } @@ -98,7 +55,12 @@ impl Command for DecodeBase32 { call: &Call, input: PipelineData, ) -> Result { - todo!() + let encoding = if call.has_flag_const(working_set, "nopad")? { + data_encoding::BASE32_NOPAD + } else { + data_encoding::BASE32 + }; + super::decode(encoding, call.span(), input) } } @@ -146,8 +108,11 @@ impl Command for EncodeBase32 { call: &Call, input: PipelineData, ) -> Result { - let config = Base32Config::from(stack, call); - let encoding = base32_encoding(config)?; + let encoding = if call.has_flag(engine_state, stack, "nopad")? { + data_encoding::BASE32_NOPAD + } else { + data_encoding::BASE32 + }; super::encode(encoding, call.span(), input) } @@ -157,7 +122,12 @@ impl Command for EncodeBase32 { call: &Call, input: PipelineData, ) -> Result { - todo!() + let encoding = if call.has_flag_const(working_set, "nopad")? { + data_encoding::BASE32_NOPAD + } else { + data_encoding::BASE32 + }; + super::encode(encoding, call.span(), input) } } From ed42fb8b92362a83dbc1483aefc6bd28c7171147 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Wed, 24 Jul 2024 12:01:44 +0300 Subject: [PATCH 07/19] Rename old Base64 commands --- crates/nu-command/src/default_context.rs | 4 ++-- .../nu-command/src/strings/encode_decode/decode_base64.rs | 6 +++--- .../nu-command/src/strings/encode_decode/encode_base64.rs | 6 +++--- crates/nu-command/src/strings/encode_decode/mod.rs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index c625dcd913..93b7a79527 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -186,8 +186,8 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Char, Decode, Encode, - DecodeBase64, - EncodeBase64, + DecodeBase64Old, + EncodeBase64Old, DetectColumns, Parse, Split, diff --git a/crates/nu-command/src/strings/encode_decode/decode_base64.rs b/crates/nu-command/src/strings/encode_decode/decode_base64.rs index cda9de4be8..0be82c503d 100644 --- a/crates/nu-command/src/strings/encode_decode/decode_base64.rs +++ b/crates/nu-command/src/strings/encode_decode/decode_base64.rs @@ -2,9 +2,9 @@ use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_D use nu_engine::command_prelude::*; #[derive(Clone)] -pub struct DecodeBase64; +pub struct DecodeBase64Old; -impl Command for DecodeBase64 { +impl Command for DecodeBase64Old { fn name(&self) -> &str { "decode base64" } @@ -114,6 +114,6 @@ mod tests { #[test] fn test_examples() { - crate::test_examples(DecodeBase64) + crate::test_examples(DecodeBase64Old) } } diff --git a/crates/nu-command/src/strings/encode_decode/encode_base64.rs b/crates/nu-command/src/strings/encode_decode/encode_base64.rs index b01530b107..eaa3bdc907 100644 --- a/crates/nu-command/src/strings/encode_decode/encode_base64.rs +++ b/crates/nu-command/src/strings/encode_decode/encode_base64.rs @@ -2,9 +2,9 @@ use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_D use nu_engine::command_prelude::*; #[derive(Clone)] -pub struct EncodeBase64; +pub struct EncodeBase64Old; -impl Command for EncodeBase64 { +impl Command for EncodeBase64Old { fn name(&self) -> &str { "encode base64" } @@ -118,6 +118,6 @@ mod tests { #[test] fn test_examples() { - crate::test_examples(EncodeBase64) + crate::test_examples(EncodeBase64Old) } } diff --git a/crates/nu-command/src/strings/encode_decode/mod.rs b/crates/nu-command/src/strings/encode_decode/mod.rs index e5d0ea06a6..5dde14e0f2 100644 --- a/crates/nu-command/src/strings/encode_decode/mod.rs +++ b/crates/nu-command/src/strings/encode_decode/mod.rs @@ -6,6 +6,6 @@ mod encode_base64; mod encoding; pub use self::decode::Decode; -pub use self::decode_base64::DecodeBase64; +pub use self::decode_base64::DecodeBase64Old; pub use self::encode::Encode; -pub use self::encode_base64::EncodeBase64; +pub use self::encode_base64::EncodeBase64Old; From 577f689d27fe9cc3358b157af69d56a15ea38fab Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Wed, 24 Jul 2024 12:14:55 +0300 Subject: [PATCH 08/19] Usage --- crates/nu-command/src/strings/base/base32.rs | 14 ++++++++------ crates/nu-command/src/strings/base/base32hex.rs | 12 ++++++++---- crates/nu-command/src/strings/base/base64.rs | 12 ++++++++---- crates/nu-command/src/strings/base/hex.rs | 12 ++---------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs index c3f392e7a3..3560239cde 100644 --- a/crates/nu-command/src/strings/base/base32.rs +++ b/crates/nu-command/src/strings/base/base32.rs @@ -2,6 +2,10 @@ use data_encoding::Encoding; use nu_engine::command_prelude::*; +const EXTRA_USAGE: &'static str = r"The default alphabet is taken from RFC 4648, section 6. + +Note this command will collect stream input."; + #[derive(Clone)] pub struct DecodeBase32; @@ -19,11 +23,11 @@ impl Command for DecodeBase32 { } fn usage(&self) -> &str { - "Decode a value." + "Decode a Base32 value." } fn extra_usage(&self) -> &str { - "TODO" + EXTRA_USAGE } fn examples(&self) -> Vec { @@ -80,17 +84,15 @@ impl Command for EncodeBase32 { ]) .allow_variants_without_examples(true) .switch("nopad", "Don't accept padding.", None) - .switch("dnscurve", "Parse as the DNSCURVE Base32 variant.", None) - .switch("dnssec", "Parse as the DNSSEC Base32 variant.", None) .category(Category::Formats) } fn usage(&self) -> &str { - "Encode a value." + "Encode a string or binary value using Base32." } fn extra_usage(&self) -> &str { - "TODO" + EXTRA_USAGE } fn examples(&self) -> Vec { diff --git a/crates/nu-command/src/strings/base/base32hex.rs b/crates/nu-command/src/strings/base/base32hex.rs index a50e1753a2..00e29797da 100644 --- a/crates/nu-command/src/strings/base/base32hex.rs +++ b/crates/nu-command/src/strings/base/base32hex.rs @@ -1,5 +1,9 @@ use nu_engine::command_prelude::*; +const EXTRA_USAGE: &'static str = r"This command uses an alternative Base32 alphabet, defined in RFC 4648, section 7. + +Note this command will collect stream input."; + #[derive(Clone)] pub struct DecodeBase32Hex; @@ -20,11 +24,11 @@ impl Command for DecodeBase32Hex { } fn usage(&self) -> &str { - "Encode a value." + "Encode a base32hex value." } fn extra_usage(&self) -> &str { - "TODO" + EXTRA_USAGE } fn examples(&self) -> Vec { @@ -87,11 +91,11 @@ impl Command for EncodeBase32Hex { } fn usage(&self) -> &str { - "Encode a value." + "Encode a binary value or a string using base32hex." } fn extra_usage(&self) -> &str { - "TODO" + EXTRA_USAGE } fn examples(&self) -> Vec { diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index d0514f8f62..298c112946 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -2,6 +2,10 @@ use data_encoding::Encoding; use nu_engine::command_prelude::*; +const EXTRA_USAGE: &'static str = r"The default alphabet is taken from RFC 4648, section 4. A URL-safe version is available. + +Note this command will collect stream input."; + fn get_encoding_from_flags(url: bool, nopad: bool) -> Encoding { match (url, nopad) { (false, false) => data_encoding::BASE64, @@ -47,11 +51,11 @@ impl Command for DecodeBase64 { } fn usage(&self) -> &str { - "TODO" + "Decode a Base64 value." } fn extra_usage(&self) -> &str { - "TODO" + EXTRA_USAGE } fn examples(&self) -> Vec { @@ -105,11 +109,11 @@ impl Command for EncodeBase64 { } fn usage(&self) -> &str { - "TODO" + "Encode a string or binary value using Base64." } fn extra_usage(&self) -> &str { - "TODO" + EXTRA_USAGE } fn examples(&self) -> Vec { diff --git a/crates/nu-command/src/strings/base/hex.rs b/crates/nu-command/src/strings/base/hex.rs index 2493b61c28..34a87abe20 100644 --- a/crates/nu-command/src/strings/base/hex.rs +++ b/crates/nu-command/src/strings/base/hex.rs @@ -16,11 +16,7 @@ impl Command for DecodeHex { } fn usage(&self) -> &str { - "TODO" - } - - fn extra_usage(&self) -> &str { - "TODO" + "Hex decode a value." } fn examples(&self) -> Vec { @@ -71,11 +67,7 @@ impl Command for EncodeHex { } fn usage(&self) -> &str { - "TODO" - } - - fn extra_usage(&self) -> &str { - "TODO" + "Hex encode a binary value or a string." } fn examples(&self) -> Vec { From 2f0b961ab3bc879dc4d89a52a1b0fe72921d950d Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Wed, 24 Jul 2024 12:53:30 +0300 Subject: [PATCH 09/19] Examples --- crates/nu-command/src/strings/base/base32.rs | 36 +++++++++++++++++-- .../nu-command/src/strings/base/base32hex.rs | 36 +++++++++++++++++-- crates/nu-command/src/strings/base/base64.rs | 36 +++++++++++++++++-- crates/nu-command/src/strings/base/hex.rs | 35 +++++++++++++++--- 4 files changed, 133 insertions(+), 10 deletions(-) diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs index 3560239cde..98c650bcbf 100644 --- a/crates/nu-command/src/strings/base/base32.rs +++ b/crates/nu-command/src/strings/base/base32.rs @@ -31,7 +31,23 @@ impl Command for DecodeBase32 { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Decode arbitrary binary data", + example: r#""AEBAGBAF" | decode base32"#, + result: Some(Value::test_binary(vec![1, 2, 3, 4, 5])), + }, + Example { + description: "Decode an encoded string", + example: r#""NBUQ====" | decode base32 | decode"#, + result: Some(Value::test_string("hi")), + }, + Example { + description: "Parse a string without padding", + example: r#""NBUQ" | decode base32 --nopad"#, + result: Some(Value::test_binary(vec![68, 69])), + }, + ] } fn is_const(&self) -> bool { @@ -96,7 +112,23 @@ impl Command for EncodeBase32 { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Encode a binary value", + example: r#"0x[01 02 10] | encode base32"#, + result: Some(Value::test_string("AEBBA===")), + }, + Example { + description: "Encode a string", + example: r#""hello there" | encode base32"#, + result: Some(Value::test_string("NBSWY3DPEB2GQZLSMU======")), + }, + Example { + description: "Don't apply padding to the output", + example: r#""hi" | encode base32 --nopad"#, + result: Some(Value::test_string("NBUQ")), + }, + ] } fn is_const(&self) -> bool { diff --git a/crates/nu-command/src/strings/base/base32hex.rs b/crates/nu-command/src/strings/base/base32hex.rs index 00e29797da..4c71a9fbaf 100644 --- a/crates/nu-command/src/strings/base/base32hex.rs +++ b/crates/nu-command/src/strings/base/base32hex.rs @@ -32,7 +32,23 @@ impl Command for DecodeBase32Hex { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Decode arbitrary binary data", + example: r#""ATNAQ===" | decode base32hex"#, + result: Some(Value::test_binary(vec![0x57, 0x6E, 0xAD])), + }, + Example { + description: "Decode an encoded string", + example: r#""D1KG====" | decode base32hex | decode"#, + result: Some(Value::test_string("hi")), + }, + Example { + description: "Parse a string without padding", + example: r#""ATNAQ" | decode base32hex --nopad"#, + result: Some(Value::test_binary(vec![0x57, 0x6E, 0xAD])), + }, + ] } fn is_const(&self) -> bool { @@ -99,7 +115,23 @@ impl Command for EncodeBase32Hex { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Encode a binary value", + example: r#"0x[57 6E AD] | encode base32hex"#, + result: Some(Value::test_string("ATNAQ===")), + }, + Example { + description: "Encode a string", + example: r#""hello there" | encode base32hex"#, + result: Some(Value::test_string("D1IMOR3F41Q6GPBICK======")), + }, + Example { + description: "Don't apply padding to the output", + example: r#""hello there" | encode base32hex"#, + result: Some(Value::test_string("D1IMOR3F41Q6GPBICK")), + }, + ] } fn is_const(&self) -> bool { diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index 298c112946..39f2407bc6 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -59,7 +59,23 @@ impl Command for DecodeBase64 { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Decode a Base64 string", + example: r#""U29tZSBEYXRh" | decode base64 | decode"#, + result: Some(Value::test_string("Some Data")), + }, + Example { + description: "Decode arbitrary data", + example: r#""/w==" | decode base64"#, + result: Some(Value::test_binary(vec![0xFF])), + }, + Example { + description: "Decode a URL-safe Base64 string", + example: r#""_w==" | decode base64 --url"#, + result: Some(Value::test_binary(vec![0xFF])), + }, + ] } fn is_const(&self) -> bool { @@ -117,7 +133,23 @@ impl Command for EncodeBase64 { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Encode a string with Base64", + example: r#""Alphabet from A to Z" | encode base64"#, + result: Some(Value::test_string("Some Data")), + }, + Example { + description: "Encode arbitrary data", + example: r#"0x[BE EE FF] | encode base64"#, + result: Some(Value::test_string("vu7/")), + }, + Example { + description: "Use a URL-safe alphabet", + example: r#"0x[BE EE FF] | encode base64 --url"#, + result: Some(Value::test_string("vu7_")), + }, + ] } fn is_const(&self) -> bool { diff --git a/crates/nu-command/src/strings/base/hex.rs b/crates/nu-command/src/strings/base/hex.rs index 34a87abe20..010406c2fb 100644 --- a/crates/nu-command/src/strings/base/hex.rs +++ b/crates/nu-command/src/strings/base/hex.rs @@ -5,11 +5,11 @@ pub struct DecodeHex; impl Command for DecodeHex { fn name(&self) -> &str { - "decode base" + "decode hex" } fn signature(&self) -> Signature { - Signature::build("decode base") + Signature::build("decode hex") .input_output_types(vec![(Type::String, Type::Binary)]) .allow_variants_without_examples(true) .category(Category::Formats) @@ -20,7 +20,18 @@ impl Command for DecodeHex { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Decode arbitrary binary data", + example: r#""09FD" | decode hex"#, + result: Some(Value::test_binary(vec![0x09, 0xFD])), + }, + Example { + description: "Lowercase Hex is also accepted", + example: r#""09fd" | decode hex"#, + result: Some(Value::test_binary(vec![0x09, 0xFD])), + }, + ] } fn is_const(&self) -> bool { @@ -71,7 +82,23 @@ impl Command for EncodeHex { } fn examples(&self) -> Vec { - vec![] + vec![ + Example { + description: "Encode a binary value", + example: r#"0x[C3 06] | encode hex"#, + result: Some(Value::test_string("C306")), + }, + Example { + description: "Encode a string", + example: r#""hello" | encode hex"#, + result: Some(Value::test_string("68656C6C6F")), + }, + Example { + description: "Output a Lowercase version of the encoding", + example: r#"0x[AD EF] | encode hex --lower"#, + result: Some(Value::test_string("adef")), + }, + ] } fn is_const(&self) -> bool { From fa9d61c280918e4b046a9a9a1ddcf77e812d40f2 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Wed, 24 Jul 2024 12:56:16 +0300 Subject: [PATCH 10/19] Format and lint --- crates/nu-command/src/default_context.rs | 15 ++++++++------- crates/nu-command/src/strings/base/base32.rs | 2 +- crates/nu-command/src/strings/base/base32hex.rs | 2 +- crates/nu-command/src/strings/base/base64.rs | 2 +- crates/nu-command/src/strings/base/mod.rs | 1 + crates/nu-command/src/strings/mod.rs | 3 ++- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 93b7a79527..ee2b5f735b 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -176,18 +176,19 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { // Strings bind_command! { - DecodeBase32, - EncodeBase32, - DecodeBase32Hex, - EncodeBase32Hex, - DecodeHex, - EncodeHex, - Char, Decode, Encode, DecodeBase64Old, EncodeBase64Old, + DecodeBase32, + EncodeBase32, + DecodeBase32Hex, + EncodeBase32Hex, + DecodeBase64, + EncodeBase64, + DecodeHex, + EncodeHex, DetectColumns, Parse, Split, diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs index 98c650bcbf..21cd0a6d02 100644 --- a/crates/nu-command/src/strings/base/base32.rs +++ b/crates/nu-command/src/strings/base/base32.rs @@ -2,7 +2,7 @@ use data_encoding::Encoding; use nu_engine::command_prelude::*; -const EXTRA_USAGE: &'static str = r"The default alphabet is taken from RFC 4648, section 6. +const EXTRA_USAGE: &str = r"The default alphabet is taken from RFC 4648, section 6. Note this command will collect stream input."; diff --git a/crates/nu-command/src/strings/base/base32hex.rs b/crates/nu-command/src/strings/base/base32hex.rs index 4c71a9fbaf..0ec77f8c4a 100644 --- a/crates/nu-command/src/strings/base/base32hex.rs +++ b/crates/nu-command/src/strings/base/base32hex.rs @@ -1,6 +1,6 @@ use nu_engine::command_prelude::*; -const EXTRA_USAGE: &'static str = r"This command uses an alternative Base32 alphabet, defined in RFC 4648, section 7. +const EXTRA_USAGE: &str = r"This command uses an alternative Base32 alphabet, defined in RFC 4648, section 7. Note this command will collect stream input."; diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index 39f2407bc6..1b6f55580e 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -2,7 +2,7 @@ use data_encoding::Encoding; use nu_engine::command_prelude::*; -const EXTRA_USAGE: &'static str = r"The default alphabet is taken from RFC 4648, section 4. A URL-safe version is available. +const EXTRA_USAGE: &str = r"The default alphabet is taken from RFC 4648, section 4. A URL-safe version is available. Note this command will collect stream input."; diff --git a/crates/nu-command/src/strings/base/mod.rs b/crates/nu-command/src/strings/base/mod.rs index 73b177f2b9..d7e248a032 100644 --- a/crates/nu-command/src/strings/base/mod.rs +++ b/crates/nu-command/src/strings/base/mod.rs @@ -11,6 +11,7 @@ mod hex; pub use base32::{DecodeBase32, EncodeBase32}; pub use base32hex::{DecodeBase32Hex, EncodeBase32Hex}; +pub use base64::{DecodeBase64, EncodeBase64}; pub use hex::{DecodeHex, EncodeHex}; pub fn decode( diff --git a/crates/nu-command/src/strings/mod.rs b/crates/nu-command/src/strings/mod.rs index deaea34146..08f50e537d 100644 --- a/crates/nu-command/src/strings/mod.rs +++ b/crates/nu-command/src/strings/mod.rs @@ -9,7 +9,8 @@ mod split; mod str_; pub use base::{ - DecodeBase32, DecodeBase32Hex, DecodeHex, EncodeBase32, EncodeBase32Hex, EncodeHex, + DecodeBase32, DecodeBase32Hex, DecodeBase64, DecodeHex, EncodeBase32, EncodeBase32Hex, + EncodeBase64, EncodeHex, }; pub use char_::Char; pub use detect_columns::*; From 7cc00968088eb2cfbf1d28cb2aede91e5af4183c Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Wed, 24 Jul 2024 13:06:27 +0300 Subject: [PATCH 11/19] Test fixes --- crates/nu-command/src/strings/base/base32hex.rs | 2 +- crates/nu-command/src/strings/base/base64.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nu-command/src/strings/base/base32hex.rs b/crates/nu-command/src/strings/base/base32hex.rs index 0ec77f8c4a..89fbd57a3a 100644 --- a/crates/nu-command/src/strings/base/base32hex.rs +++ b/crates/nu-command/src/strings/base/base32hex.rs @@ -128,7 +128,7 @@ impl Command for EncodeBase32Hex { }, Example { description: "Don't apply padding to the output", - example: r#""hello there" | encode base32hex"#, + example: r#""hello there" | encode base32hex --nopad"#, result: Some(Value::test_string("D1IMOR3F41Q6GPBICK")), }, ] diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index 1b6f55580e..c859c64fcc 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -137,7 +137,7 @@ impl Command for EncodeBase64 { Example { description: "Encode a string with Base64", example: r#""Alphabet from A to Z" | encode base64"#, - result: Some(Value::test_string("Some Data")), + result: Some(Value::test_string("QWxwaGFiZXQgZnJvbSBBIHRvIFo=")), }, Example { description: "Encode arbitrary data", From a4414e41ec65bcbac07ae998f69de2d6212699cf Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Thu, 1 Aug 2024 15:32:28 +0300 Subject: [PATCH 12/19] Add a `new-` prefix to base64 commands --- crates/nu-command/src/strings/base/base64.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index c859c64fcc..38f6dac86f 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -38,7 +38,7 @@ pub struct DecodeBase64; impl Command for DecodeBase64 { fn name(&self) -> &str { - "decode base64" + "decode new-base64" } fn signature(&self) -> Signature { @@ -109,7 +109,7 @@ pub struct EncodeBase64; impl Command for EncodeBase64 { fn name(&self) -> &str { - "encode base64" + "encode new-base64" } fn signature(&self) -> Signature { From 7cf7cdaffdd221718871fe305499a348506a3485 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Thu, 1 Aug 2024 15:40:27 +0300 Subject: [PATCH 13/19] DeprecatedWarning --- .../src/strings/encode_decode/decode_base64.rs | 11 +++++++++++ .../src/strings/encode_decode/encode_base64.rs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/crates/nu-command/src/strings/encode_decode/decode_base64.rs b/crates/nu-command/src/strings/encode_decode/decode_base64.rs index 0be82c503d..b4aa34d1e8 100644 --- a/crates/nu-command/src/strings/encode_decode/decode_base64.rs +++ b/crates/nu-command/src/strings/encode_decode/decode_base64.rs @@ -1,5 +1,6 @@ use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_DESC}; use nu_engine::command_prelude::*; +use nu_protocol::{report_warning_new, ParseWarning}; #[derive(Clone)] pub struct DecodeBase64Old; @@ -77,6 +78,16 @@ impl Command for DecodeBase64Old { call: &Call, input: PipelineData, ) -> Result { + report_warning_new( + engine_state, + &ParseWarning::DeprecatedWarning { + old_command: "decode base64".into(), + new_suggestion: "the new `decode new-base64` version".into(), + span: call.head, + url: "`help decode new-base64`".into(), + }, + ); + let character_set: Option> = call.get_flag(engine_state, stack, "character-set")?; let binary = call.has_flag(engine_state, stack, "binary")?; diff --git a/crates/nu-command/src/strings/encode_decode/encode_base64.rs b/crates/nu-command/src/strings/encode_decode/encode_base64.rs index eaa3bdc907..cb9ad1dbb1 100644 --- a/crates/nu-command/src/strings/encode_decode/encode_base64.rs +++ b/crates/nu-command/src/strings/encode_decode/encode_base64.rs @@ -1,5 +1,6 @@ use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_DESC}; use nu_engine::command_prelude::*; +use nu_protocol::{report_warning_new, ParseWarning}; #[derive(Clone)] pub struct EncodeBase64Old; @@ -81,6 +82,16 @@ impl Command for EncodeBase64Old { call: &Call, input: PipelineData, ) -> Result { + report_warning_new( + engine_state, + &ParseWarning::DeprecatedWarning { + old_command: "encode base64".into(), + new_suggestion: "the new `encode new-base64` version".into(), + span: call.head, + url: "`help encode new-base64`".into(), + }, + ); + let character_set: Option> = call.get_flag(engine_state, stack, "character-set")?; let binary = call.has_flag(engine_state, stack, "binary")?; From 318d6a4a2a7044757d75d59556b1c22ccb687f0f Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Thu, 1 Aug 2024 15:41:13 +0300 Subject: [PATCH 14/19] Update new signatures --- crates/nu-command/src/strings/base/base64.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index 38f6dac86f..6c914c75c3 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -42,7 +42,7 @@ impl Command for DecodeBase64 { } fn signature(&self) -> Signature { - Signature::build("decode base64") + Signature::build("decode new-base64") .input_output_types(vec![(Type::String, Type::Binary)]) .allow_variants_without_examples(true) .switch("url", "Decode the URL-safe Base64 version.", None) @@ -113,7 +113,7 @@ impl Command for EncodeBase64 { } fn signature(&self) -> Signature { - Signature::build("encode base64") + Signature::build("encode new-base64") .input_output_types(vec![ (Type::String, Type::String), (Type::Binary, Type::String), From be816e52d740df5bfeedbfc82e159384ec6a0ad5 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Thu, 1 Aug 2024 22:45:33 +0300 Subject: [PATCH 15/19] Fix tests --- crates/nu-command/src/strings/base/base32.rs | 5 ++--- crates/nu-command/src/strings/base/base32hex.rs | 6 ++---- crates/nu-command/src/strings/base/base64.rs | 15 +++++++-------- crates/nu-command/src/strings/base/hex.rs | 2 -- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/crates/nu-command/src/strings/base/base32.rs b/crates/nu-command/src/strings/base/base32.rs index 21cd0a6d02..8c3875f10b 100644 --- a/crates/nu-command/src/strings/base/base32.rs +++ b/crates/nu-command/src/strings/base/base32.rs @@ -40,12 +40,12 @@ impl Command for DecodeBase32 { Example { description: "Decode an encoded string", example: r#""NBUQ====" | decode base32 | decode"#, - result: Some(Value::test_string("hi")), + result: None, }, Example { description: "Parse a string without padding", example: r#""NBUQ" | decode base32 --nopad"#, - result: Some(Value::test_binary(vec![68, 69])), + result: Some(Value::test_binary(vec![0x68, 0x69])), }, ] } @@ -98,7 +98,6 @@ impl Command for EncodeBase32 { (Type::String, Type::String), (Type::Binary, Type::String), ]) - .allow_variants_without_examples(true) .switch("nopad", "Don't accept padding.", None) .category(Category::Formats) } diff --git a/crates/nu-command/src/strings/base/base32hex.rs b/crates/nu-command/src/strings/base/base32hex.rs index 89fbd57a3a..f8883ae8e7 100644 --- a/crates/nu-command/src/strings/base/base32hex.rs +++ b/crates/nu-command/src/strings/base/base32hex.rs @@ -15,8 +15,7 @@ impl Command for DecodeBase32Hex { fn signature(&self) -> Signature { Signature::build("decode base32hex") .input_output_types(vec![ - (Type::String, Type::String), - (Type::Binary, Type::String), + (Type::String, Type::Binary), ]) .allow_variants_without_examples(true) .switch("nopad", "Reject input with padding.", None) @@ -41,7 +40,7 @@ impl Command for DecodeBase32Hex { Example { description: "Decode an encoded string", example: r#""D1KG====" | decode base32hex | decode"#, - result: Some(Value::test_string("hi")), + result: None, }, Example { description: "Parse a string without padding", @@ -101,7 +100,6 @@ impl Command for EncodeBase32Hex { (Type::String, Type::String), (Type::Binary, Type::String), ]) - .allow_variants_without_examples(true) .switch("nopad", "Don't pad the output.", None) .category(Category::Formats) } diff --git a/crates/nu-command/src/strings/base/base64.rs b/crates/nu-command/src/strings/base/base64.rs index 6c914c75c3..86ff1c15cd 100644 --- a/crates/nu-command/src/strings/base/base64.rs +++ b/crates/nu-command/src/strings/base/base64.rs @@ -62,17 +62,17 @@ impl Command for DecodeBase64 { vec![ Example { description: "Decode a Base64 string", - example: r#""U29tZSBEYXRh" | decode base64 | decode"#, - result: Some(Value::test_string("Some Data")), + example: r#""U29tZSBEYXRh" | decode new-base64 | decode"#, + result: None, }, Example { description: "Decode arbitrary data", - example: r#""/w==" | decode base64"#, + example: r#""/w==" | decode new-base64"#, result: Some(Value::test_binary(vec![0xFF])), }, Example { description: "Decode a URL-safe Base64 string", - example: r#""_w==" | decode base64 --url"#, + example: r#""_w==" | decode new-base64 --url"#, result: Some(Value::test_binary(vec![0xFF])), }, ] @@ -118,7 +118,6 @@ impl Command for EncodeBase64 { (Type::String, Type::String), (Type::Binary, Type::String), ]) - .allow_variants_without_examples(true) .switch("url", "Use the URL-safe Base64 version.", None) .switch("nopad", "Don't pad the output.", None) .category(Category::Formats) @@ -136,17 +135,17 @@ impl Command for EncodeBase64 { vec![ Example { description: "Encode a string with Base64", - example: r#""Alphabet from A to Z" | encode base64"#, + example: r#""Alphabet from A to Z" | encode new-base64"#, result: Some(Value::test_string("QWxwaGFiZXQgZnJvbSBBIHRvIFo=")), }, Example { description: "Encode arbitrary data", - example: r#"0x[BE EE FF] | encode base64"#, + example: r#"0x[BE EE FF] | encode new-base64"#, result: Some(Value::test_string("vu7/")), }, Example { description: "Use a URL-safe alphabet", - example: r#"0x[BE EE FF] | encode base64 --url"#, + example: r#"0x[BE EE FF] | encode new-base64 --url"#, result: Some(Value::test_string("vu7_")), }, ] diff --git a/crates/nu-command/src/strings/base/hex.rs b/crates/nu-command/src/strings/base/hex.rs index 010406c2fb..af8f9c78d3 100644 --- a/crates/nu-command/src/strings/base/hex.rs +++ b/crates/nu-command/src/strings/base/hex.rs @@ -11,7 +11,6 @@ impl Command for DecodeHex { fn signature(&self) -> Signature { Signature::build("decode hex") .input_output_types(vec![(Type::String, Type::Binary)]) - .allow_variants_without_examples(true) .category(Category::Formats) } @@ -72,7 +71,6 @@ impl Command for EncodeHex { (Type::String, Type::String), (Type::Binary, Type::String), ]) - .allow_variants_without_examples(true) .switch("lower", "Encode to lowercase hex.", None) .category(Category::Formats) } From bbc633f42a739d679f498070bbf8b95e3912e27c Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Thu, 1 Aug 2024 23:02:23 +0300 Subject: [PATCH 16/19] Fix formatting --- crates/nu-command/src/strings/base/base32hex.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/nu-command/src/strings/base/base32hex.rs b/crates/nu-command/src/strings/base/base32hex.rs index f8883ae8e7..597dc2417b 100644 --- a/crates/nu-command/src/strings/base/base32hex.rs +++ b/crates/nu-command/src/strings/base/base32hex.rs @@ -14,9 +14,7 @@ impl Command for DecodeBase32Hex { fn signature(&self) -> Signature { Signature::build("decode base32hex") - .input_output_types(vec![ - (Type::String, Type::Binary), - ]) + .input_output_types(vec![(Type::String, Type::Binary)]) .allow_variants_without_examples(true) .switch("nopad", "Reject input with padding.", None) .category(Category::Formats) From eec1c881f37040846396f54327e9614fd4945cb9 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Mon, 5 Aug 2024 14:51:59 +0300 Subject: [PATCH 17/19] Fix binding indentation --- crates/nu-command/src/default_context.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index ee2b5f735b..acf0eb24fe 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -179,16 +179,16 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Char, Decode, Encode, - DecodeBase64Old, - EncodeBase64Old, - DecodeBase32, + DecodeHex, + EncodeHex, + DecodeBase32, EncodeBase32, DecodeBase32Hex, EncodeBase32Hex, DecodeBase64, EncodeBase64, - DecodeHex, - EncodeHex, + DecodeBase64Old, + EncodeBase64Old, DetectColumns, Parse, Split, From 6b994ce2dfbcbca720eaf528f04dafc1e96f26a5 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Mon, 5 Aug 2024 16:05:59 +0300 Subject: [PATCH 18/19] Add tests --- Cargo.lock | 1 + crates/nu-command/Cargo.toml | 1 + .../nu-command/tests/commands/base/base32.rs | 58 +++++++++++++ .../tests/commands/base/base32hex.rs | 58 +++++++++++++ .../nu-command/tests/commands/base/base64.rs | 86 +++++++++++++++++++ crates/nu-command/tests/commands/base/hex.rs | 36 ++++++++ crates/nu-command/tests/commands/base/mod.rs | 24 ++++++ crates/nu-command/tests/commands/mod.rs | 1 + 8 files changed, 265 insertions(+) create mode 100644 crates/nu-command/tests/commands/base/base32.rs create mode 100644 crates/nu-command/tests/commands/base/base32hex.rs create mode 100644 crates/nu-command/tests/commands/base/base64.rs create mode 100644 crates/nu-command/tests/commands/base/hex.rs create mode 100644 crates/nu-command/tests/commands/base/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 94ec5cfee7..093162409c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3105,6 +3105,7 @@ dependencies = [ "quickcheck", "quickcheck_macros", "rand", + "rand_chacha", "rayon", "regex", "rmp", diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index 2af5192056..5b61536673 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -148,3 +148,4 @@ quickcheck_macros = { workspace = true } rstest = { workspace = true, default-features = false } pretty_assertions = { workspace = true } tempfile = { workspace = true } +rand_chacha = "0.3.1" diff --git a/crates/nu-command/tests/commands/base/base32.rs b/crates/nu-command/tests/commands/base/base32.rs new file mode 100644 index 0000000000..e6fae31808 --- /dev/null +++ b/crates/nu-command/tests/commands/base/base32.rs @@ -0,0 +1,58 @@ +use nu_test_support::nu; + +#[test] +fn canonical() { + for value in super::random_bytes() { + let outcome = nu!("{} | encode base32 | decode base32 | to nuon", value); + assert_eq!(outcome.out, value); + + let outcome = nu!( + "{} | encode base32 --nopad | decode base32 --nopad | to nuon", + value + ); + assert_eq!(outcome.out, value); + } +} + +#[test] +fn encode() { + let text = "Ș̗͙̂̏o̲̲̗͗̌͊m̝̊̓́͂ë̡̦̞̤́̌̈́̀ ̥̝̪̎̿ͅf̧̪̻͉͗̈́̍̆u̮̝͌̈́ͅn̹̞̈́̊k̮͇̟͎̂͘y̧̲̠̾̆̕ͅ ̙͖̭͔̂̐t̞́́͘e̢̨͕̽x̥͋t͍̑̔͝"; + let encoded = "KPGIFTEPZSTMZF6NTFX43F6MRTGYVTFSZSZMZF3NZSFMZE6MQHGYFTE5MXGYJTEMZWCMZAGMU3GKDTE6ZSSCBTEOZS743BOMUXGJ3TFKM3GZPTMEZSG4ZBWMVLGLXTFHZWEXLTMMZWCMZLWMTXGYK3WNQTGIVTFZZSPGXTMYZSBMZLWNQ7GJ7TMOPHGL5TEVZSDM3BOMWLGKPTFAEDGIFTEQZSM43FWMVXGZI5GMQHGZRTEBZSPGLTF5ZSRM3FOMVB4M3C6MUV2MZEOMSTGZ3TMN"; + + let outcome = nu!("'{}' | encode base32 --nopad", text); + assert_eq!(outcome.out, encoded); +} + +#[test] +fn decode_string() { + let text = "Very important data"; + let encoded = "KZSXE6JANFWXA33SORQW45BAMRQXIYI="; + + let outcome = nu!("'{}' | decode base32 | decode", encoded); + assert_eq!(outcome.out, text); +} + +#[test] +fn decode_pad_nopad() { + let text = "®lnnE¾ˆë"; + let encoded_pad = "YKXGY3TOIXBL5S4GYOVQ===="; + let encoded_nopad = "YKXGY3TOIXBL5S4GYOVQ"; + + let outcome = nu!("'{}' | decode base32 | decode", encoded_pad); + assert_eq!(outcome.out, text); + + let outcome = nu!("'{}' | decode base32 --nopad | decode", encoded_nopad); + assert_eq!(outcome.out, text); +} + +#[test] +fn reject_pad_nopad() { + let encoded_nopad = "ME"; + let encoded_pad = "ME======"; + + let outcome = nu!("'{}' | decode base32", encoded_nopad); + assert!(!outcome.err.is_empty()); + + let outcome = nu!("'{}' | decode base32 --nopad", encoded_pad); + assert!(!outcome.err.is_empty()) +} diff --git a/crates/nu-command/tests/commands/base/base32hex.rs b/crates/nu-command/tests/commands/base/base32hex.rs new file mode 100644 index 0000000000..adb1494bd9 --- /dev/null +++ b/crates/nu-command/tests/commands/base/base32hex.rs @@ -0,0 +1,58 @@ +use nu_test_support::nu; + +#[test] +fn canonical() { + for value in super::random_bytes() { + let outcome = nu!("{} | encode base32hex | decode base32hex | to nuon", value); + assert_eq!(outcome.out, value); + + let outcome = nu!( + "{} | encode base32hex --nopad | decode base32hex --nopad | to nuon", + value + ); + assert_eq!(outcome.out, value); + } +} + +#[test] +fn encode() { + let text = "Ș̗͙̂̏o̲̲̗͗̌͊m̝̊̓́͂ë̡̦̞̤́̌̈́̀ ̥̝̪̎̿ͅf̧̪̻͉͗̈́̍̆u̮̝͌̈́ͅn̹̞̈́̊k̮͇̟͎̂͘y̧̲̠̾̆̕ͅ ̙͖̭͔̂̐t̞́́͘e̢̨͕̽x̥͋t͍̑̔͝"; + let encoded = "AF685J4FPIJCP5UDJ5NSR5UCHJ6OLJ5IPIPCP5RDPI5CP4UCG76O5J4TCN6O9J4CPM2CP06CKR6A3J4UPII21J4EPIVSR1ECKN69RJ5ACR6PFJC4PI6SP1MCLB6BNJ57PM4NBJCCPM2CPBMCJN6OARMDGJ68LJ5PPIF6NJCOPI1CPBMDGV69VJCEF76BTJ4LPI3CR1ECMB6AFJ5043685J4GPICSR5MCLN6P8T6CG76PHJ41PIF6BJ5TPIHCR5ECL1SCR2UCKLQCP4ECIJ6PRJCD"; + + let outcome = nu!("'{}' | encode base32hex --nopad", text); + assert_eq!(outcome.out, encoded); +} + +#[test] +fn decode_string() { + let text = "Very important data"; + let encoded = "APIN4U90D5MN0RRIEHGMST10CHGN8O8="; + + let outcome = nu!("'{}' | decode base32hex | decode", encoded); + assert_eq!(outcome.out, text); +} + +#[test] +fn decode_pad_nopad() { + let text = "®lnnE¾ˆë"; + let encoded_pad = "OAN6ORJE8N1BTIS6OELG===="; + let encoded_nopad = "OAN6ORJE8N1BTIS6OELG"; + + let outcome = nu!("'{}' | decode base32hex | decode", encoded_pad); + assert_eq!(outcome.out, text); + + let outcome = nu!("'{}' | decode base32hex --nopad | decode", encoded_nopad); + assert_eq!(outcome.out, text); +} + +#[test] +fn reject_pad_nopad() { + let encoded_nopad = "D1KG"; + let encoded_pad = "D1KG===="; + + let outcome = nu!("'{}' | decode base32hex", encoded_nopad); + assert!(!outcome.err.is_empty()); + + let outcome = nu!("'{}' | decode base32hex --nopad", encoded_pad); + assert!(!outcome.err.is_empty()) +} diff --git a/crates/nu-command/tests/commands/base/base64.rs b/crates/nu-command/tests/commands/base/base64.rs new file mode 100644 index 0000000000..4f9264be36 --- /dev/null +++ b/crates/nu-command/tests/commands/base/base64.rs @@ -0,0 +1,86 @@ +use nu_test_support::nu; + +#[test] +fn canonical() { + for value in super::random_bytes() { + let outcome = nu!( + "{} | encode new-base64 | decode new-base64 | to nuon", + value + ); + assert_eq!(outcome.out, value); + + let outcome = nu!( + "{} | encode new-base64 --url | decode new-base64 --url | to nuon", + value + ); + assert_eq!(outcome.out, value); + + let outcome = nu!( + "{} | encode new-base64 --nopad | decode new-base64 --nopad | to nuon", + value + ); + assert_eq!(outcome.out, value); + + let outcome = nu!( + "{} | encode new-base64 --url --nopad | decode new-base64 --url --nopad | to nuon", + value + ); + assert_eq!(outcome.out, value); + } +} + +#[test] +fn encode() { + let text = "Ș̗͙̂̏o̲̲̗͗̌͊m̝̊̓́͂ë̡̦̞̤́̌̈́̀ ̥̝̪̎̿ͅf̧̪̻͉͗̈́̍̆u̮̝͌̈́ͅn̹̞̈́̊k̮͇̟͎̂͘y̧̲̠̾̆̕ͅ ̙͖̭͔̂̐t̞́́͘e̢̨͕̽x̥͋t͍̑̔͝"; + let encoded = "U8yCzI/MpsyXzZlvzZfMjM2KzLLMssyXbcyKzJPMgc2CzJ1lzYTMjM2EzIDMpsyhzJ7MpCDMjsy/zYXMpcydzKpmzZfNhMyNzIbMqsy7zKfNiXXNjM2EzK7Mnc2Fbs2EzIrMucyea82YzILMrs2HzJ/NjnnMvsyVzIbNhcyyzKfMoCDMgsyQzJnNlsytzZR0zIHNmMyBzJ5lzL3Mos2VzKh4zYvMpXTMkcyUzZ3NjQ=="; + + let outcome = nu!("'{}' | encode new-base64", text); + assert_eq!(outcome.out, encoded); +} + +#[test] +fn decode_string() { + let text = "Very important data"; + let encoded = "VmVyeSBpbXBvcnRhbnQgZGF0YQ=="; + + let outcome = nu!("'{}' | decode new-base64 | decode", encoded); + assert_eq!(outcome.out, text); +} + +#[test] +fn decode_pad_nopad() { + let text = "”¥.ä@°bZö¢"; + let encoded_pad = "4oCdwqUuw6RAwrBiWsO2wqI="; + let encoded_nopad = "4oCdwqUuw6RAwrBiWsO2wqI"; + + let outcome = nu!("'{}' | decode new-base64 | decode", encoded_pad); + assert_eq!(outcome.out, text); + + let outcome = nu!("'{}' | decode new-base64 --nopad | decode", encoded_nopad); + assert_eq!(outcome.out, text); +} + +#[test] +fn decode_url() { + let text = "ޘ::߇3/Ծ]D"; + let encoded = "3pg6Ot+HMy/Uvl1E"; + let encoded_url = "3pg6Ot-HMy_Uvl1E"; + + let outcome = nu!("'{}' | decode new-base64 | decode", encoded); + assert_eq!(outcome.out, text); + + let outcome = nu!("'{}' | decode new-base64 --url | decode", encoded_url); + assert_eq!(outcome.out, text); +} + +#[test] +fn reject_pad_nopad() { + let encoded_nopad = "YQ"; + let encoded_pad = "YQ=="; + + let outcome = nu!("'{}' | decode new-base64", encoded_nopad); + assert!(!outcome.err.is_empty()); + + let outcome = nu!("'{}' | decode new-base64 --nopad", encoded_pad); + assert!(!outcome.err.is_empty()) +} diff --git a/crates/nu-command/tests/commands/base/hex.rs b/crates/nu-command/tests/commands/base/hex.rs new file mode 100644 index 0000000000..5f497408b6 --- /dev/null +++ b/crates/nu-command/tests/commands/base/hex.rs @@ -0,0 +1,36 @@ +use nu_test_support::nu; + +#[test] +fn canonical() { + for value in super::random_bytes() { + let outcome = nu!("{} | encode hex | decode hex | to nuon", value); + assert_eq!(outcome.out, value); + } +} + +#[test] +fn encode() { + let text = "Ș̗͙̂̏o̲̲̗͗̌͊m̝̊̓́͂ë̡̦̞̤́̌̈́̀ ̥̝̪̎̿ͅf̧̪̻͉͗̈́̍̆u̮̝͌̈́ͅn̹̞̈́̊k̮͇̟͎̂͘y̧̲̠̾̆̕ͅ ̙͖̭͔̂̐t̞́́͘e̢̨͕̽x̥͋t͍̑̔͝"; + let encoded = "53CC82CC8FCCA6CC97CD996FCD97CC8CCD8ACCB2CCB2CC976DCC8ACC93CC81CD82CC9D65CD84CC8CCD84CC80CCA6CCA1CC9ECCA420CC8ECCBFCD85CCA5CC9DCCAA66CD97CD84CC8DCC86CCAACCBBCCA7CD8975CD8CCD84CCAECC9DCD856ECD84CC8ACCB9CC9E6BCD98CC82CCAECD87CC9FCD8E79CCBECC95CC86CD85CCB2CCA7CCA020CC82CC90CC99CD96CCADCD9474CC81CD98CC81CC9E65CCBDCCA2CD95CCA878CD8BCCA574CC91CC94CD9DCD8D"; + + let outcome = nu!("'{}' | encode hex", text); + assert_eq!(outcome.out, encoded); +} + +#[test] +fn decode_string() { + let text = "Very important data"; + let encoded = "5665727920696D706F7274616E742064617461"; + + let outcome = nu!("'{}' | decode hex | decode", encoded); + assert_eq!(outcome.out, text); +} + +#[test] +fn decode_case_mixing() { + let text = "®lnnE¾ˆë"; + let mixed_encoded = "c2aE6c6e6E45C2BeCB86c3ab"; + + let outcome = nu!("'{}' | decode hex | decode", mixed_encoded); + assert_eq!(outcome.out, text); +} diff --git a/crates/nu-command/tests/commands/base/mod.rs b/crates/nu-command/tests/commands/base/mod.rs new file mode 100644 index 0000000000..09828c9211 --- /dev/null +++ b/crates/nu-command/tests/commands/base/mod.rs @@ -0,0 +1,24 @@ +use data_encoding::HEXUPPER; +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; + +mod base32; +mod base32hex; +mod base64; +mod hex; + +/// Generate a few random binaries. +pub fn random_bytes() -> Vec { + const NUM: usize = 32; + let mut rng = ChaCha8Rng::seed_from_u64(4); + + (0..NUM) + .map(|_| { + let length = rng.gen_range(0..512); + let mut bytes = vec![0u8; length]; + rng.fill_bytes(&mut bytes); + let hex_bytes = HEXUPPER.encode(&bytes); + format!("0x[{}]", hex_bytes) + }) + .collect() +} diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index 3cb468ad30..63911ebfbc 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -3,6 +3,7 @@ mod all; mod any; mod append; mod assignment; +mod base; mod break_; mod bytes; mod cal; From d6fbd418ff81ae7f7dc09a4aff57b01af09cf9d3 Mon Sep 17 00:00:00 2001 From: Andrej Kolchin Date: Mon, 5 Aug 2024 18:40:19 +0300 Subject: [PATCH 19/19] Change test to avoid `typos` CI false positive --- crates/nu-command/tests/commands/base/base64.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/nu-command/tests/commands/base/base64.rs b/crates/nu-command/tests/commands/base/base64.rs index 4f9264be36..12b5f49fff 100644 --- a/crates/nu-command/tests/commands/base/base64.rs +++ b/crates/nu-command/tests/commands/base/base64.rs @@ -62,9 +62,9 @@ fn decode_pad_nopad() { #[test] fn decode_url() { - let text = "ޘ::߇3/Ծ]D"; - let encoded = "3pg6Ot+HMy/Uvl1E"; - let encoded_url = "3pg6Ot-HMy_Uvl1E"; + let text = "p:gטݾ߫t+?"; + let encoded = "cDpn15jdvt+rdCs/"; + let encoded_url = "cDpn15jdvt-rdCs_"; let outcome = nu!("'{}' | decode new-base64 | decode", encoded); assert_eq!(outcome.out, text);