diff --git a/Cargo.lock b/Cargo.lock index 0b11e93386..9d2b9cebcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2565,6 +2565,7 @@ dependencies = [ "Inflector", "alphanumeric-sort", "base64", + "byteorder", "bytesize", "calamine", "chrono", diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index 8f85469d51..bcc7088c10 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -28,6 +28,7 @@ nu-ansi-term = "0.46.0" # Potential dependencies for extras alphanumeric-sort = "1.4.4" base64 = "0.13.0" +byteorder = "1.4.3" bytesize = "1.1.0" calamine = "0.18.0" chrono = { version = "0.4.19", features = ["serde"] } diff --git a/crates/nu-command/src/conversions/into/int.rs b/crates/nu-command/src/conversions/into/int.rs index 2a309fb96d..7db84cff0f 100644 --- a/crates/nu-command/src/conversions/into/int.rs +++ b/crates/nu-command/src/conversions/into/int.rs @@ -8,6 +8,7 @@ use nu_protocol::{ struct Arguments { radix: Option, column_paths: Vec, + little_endian: bool, } #[derive(Clone)] @@ -21,6 +22,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into int") .named("radix", SyntaxShape::Number, "radix of integer", Some('r')) + .switch("little-endian", "use little-endian byte decoding", None) .rest( "rest", SyntaxShape::CellPath, @@ -114,6 +116,7 @@ fn into_int( let options = Arguments { radix: call.get_flag(engine_state, stack, "radix")?, + little_endian: call.has_flag("little-endian"), column_paths: call.rest(engine_state, stack, 0)?, }; @@ -135,13 +138,13 @@ fn into_int( input.map( move |v| { if options.column_paths.is_empty() { - action(&v, head, radix) + action(&v, head, radix, options.little_endian) } else { let mut ret = v; for path in &options.column_paths { let r = ret.update_cell_path( &path.members, - Box::new(move |old| action(old, head, radix)), + Box::new(move |old| action(old, head, radix, options.little_endian)), ); if let Err(error) = r { return Value::Error { error }; @@ -155,7 +158,7 @@ fn into_int( ) } -pub fn action(input: &Value, span: Span, radix: u32) -> Value { +pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value { match input { Value::Int { val: _, .. } => { if radix == 10 { @@ -190,6 +193,33 @@ pub fn action(input: &Value, span: Span, radix: u32) -> Value { val: val.timestamp(), span, }, + Value::Binary { val, span } => { + use byteorder::{BigEndian, ByteOrder, LittleEndian}; + + let mut val = val.to_vec(); + + if little_endian { + while val.len() < 8 { + val.push(0); + } + val.resize(8, 0); + + Value::Int { + val: LittleEndian::read_i64(&val), + span: *span, + } + } else { + while val.len() < 8 { + val.insert(0, 0); + } + val.resize(8, 0); + + Value::Int { + val: BigEndian::read_i64(&val), + span: *span, + } + } + } _ => Value::Error { error: ShellError::UnsupportedInput( format!("'into int' for unsupported type '{}'", input.get_type()), @@ -294,21 +324,21 @@ mod test { let word = Value::test_string("10"); let expected = Value::test_int(10); - let actual = action(&word, Span::test_data(), 10); + let actual = action(&word, Span::test_data(), 10, false); assert_eq!(actual, expected); } #[test] fn turns_binary_to_integer() { let s = Value::test_string("0b101"); - let actual = action(&s, Span::test_data(), 10); + let actual = action(&s, Span::test_data(), 10, false); assert_eq!(actual, Value::test_int(5)); } #[test] fn turns_hex_to_integer() { let s = Value::test_string("0xFF"); - let actual = action(&s, Span::test_data(), 16); + let actual = action(&s, Span::test_data(), 16, false); assert_eq!(actual, Value::test_int(255)); } @@ -316,7 +346,7 @@ mod test { fn communicates_parsing_error_given_an_invalid_integerlike_string() { let integer_str = Value::test_string("36anra"); - let actual = action(&integer_str, Span::test_data(), 10); + let actual = action(&integer_str, Span::test_data(), 10, false); assert_eq!(actual.get_type(), Error) } diff --git a/crates/nu-command/tests/commands/into_int.rs b/crates/nu-command/tests/commands/into_int.rs index e7f5d7c5d9..0220442a68 100644 --- a/crates/nu-command/tests/commands/into_int.rs +++ b/crates/nu-command/tests/commands/into_int.rs @@ -35,3 +35,15 @@ fn into_int_int() { assert!(actual.out.contains('1')); } + +#[test] +fn into_int_binary() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 0x[01010101] | into int + "# + )); + + assert!(actual.out.contains("16843009")); +}