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::*;