Binary into int (#5941)

* Add support for binary to into int

* Add test
This commit is contained in:
JT 2022-07-04 06:31:50 +12:00 committed by GitHub
parent 4e90b478b7
commit 2ac5b0480a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 7 deletions

1
Cargo.lock generated
View file

@ -2565,6 +2565,7 @@ dependencies = [
"Inflector", "Inflector",
"alphanumeric-sort", "alphanumeric-sort",
"base64", "base64",
"byteorder",
"bytesize", "bytesize",
"calamine", "calamine",
"chrono", "chrono",

View file

@ -28,6 +28,7 @@ nu-ansi-term = "0.46.0"
# Potential dependencies for extras # Potential dependencies for extras
alphanumeric-sort = "1.4.4" alphanumeric-sort = "1.4.4"
base64 = "0.13.0" base64 = "0.13.0"
byteorder = "1.4.3"
bytesize = "1.1.0" bytesize = "1.1.0"
calamine = "0.18.0" calamine = "0.18.0"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }

View file

@ -8,6 +8,7 @@ use nu_protocol::{
struct Arguments { struct Arguments {
radix: Option<Value>, radix: Option<Value>,
column_paths: Vec<CellPath>, column_paths: Vec<CellPath>,
little_endian: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -21,6 +22,7 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into int") Signature::build("into int")
.named("radix", SyntaxShape::Number, "radix of integer", Some('r')) .named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
.switch("little-endian", "use little-endian byte decoding", None)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
@ -114,6 +116,7 @@ fn into_int(
let options = Arguments { let options = Arguments {
radix: call.get_flag(engine_state, stack, "radix")?, radix: call.get_flag(engine_state, stack, "radix")?,
little_endian: call.has_flag("little-endian"),
column_paths: call.rest(engine_state, stack, 0)?, column_paths: call.rest(engine_state, stack, 0)?,
}; };
@ -135,13 +138,13 @@ fn into_int(
input.map( input.map(
move |v| { move |v| {
if options.column_paths.is_empty() { if options.column_paths.is_empty() {
action(&v, head, radix) action(&v, head, radix, options.little_endian)
} else { } else {
let mut ret = v; let mut ret = v;
for path in &options.column_paths { for path in &options.column_paths {
let r = ret.update_cell_path( let r = ret.update_cell_path(
&path.members, &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 { if let Err(error) = r {
return Value::Error { error }; 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 { match input {
Value::Int { val: _, .. } => { Value::Int { val: _, .. } => {
if radix == 10 { if radix == 10 {
@ -190,6 +193,33 @@ pub fn action(input: &Value, span: Span, radix: u32) -> Value {
val: val.timestamp(), val: val.timestamp(),
span, 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 { _ => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::UnsupportedInput(
format!("'into int' for unsupported type '{}'", input.get_type()), format!("'into int' for unsupported type '{}'", input.get_type()),
@ -294,21 +324,21 @@ mod test {
let word = Value::test_string("10"); let word = Value::test_string("10");
let expected = Value::test_int(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); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_binary_to_integer() { fn turns_binary_to_integer() {
let s = Value::test_string("0b101"); 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)); assert_eq!(actual, Value::test_int(5));
} }
#[test] #[test]
fn turns_hex_to_integer() { fn turns_hex_to_integer() {
let s = Value::test_string("0xFF"); 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)); assert_eq!(actual, Value::test_int(255));
} }
@ -316,7 +346,7 @@ mod test {
fn communicates_parsing_error_given_an_invalid_integerlike_string() { fn communicates_parsing_error_given_an_invalid_integerlike_string() {
let integer_str = Value::test_string("36anra"); 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) assert_eq!(actual.get_type(), Error)
} }

View file

@ -35,3 +35,15 @@ fn into_int_int() {
assert!(actual.out.contains('1')); 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"));
}