forked from extern/nushell
Fix 9156 endian consistency (#9873)
- fixed #9156 # Description I'm trying to fix the problems mentioned in the issue. It's my first attempt in Rust. Please let me know if there are any problems. # User-Facing Changes - The `--little-endian` option dropped, replaced with `--endian`. - Add the `--compact` option to the `into binary` command. - `into int` accepts binary input
This commit is contained in:
parent
d4eeef4bd1
commit
a785e64bc9
@ -9,6 +9,7 @@ use nu_protocol::{
|
|||||||
|
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
|
compact: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
@ -39,6 +40,7 @@ impl Command for SubCommand {
|
|||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::Record(vec![]), Type::Record(vec![])),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
|
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
|
||||||
|
.switch("compact", "output without padding zeros", Some('c'))
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
@ -82,7 +84,7 @@ impl Command for SubCommand {
|
|||||||
description: "convert a number to a nushell binary primitive",
|
description: "convert a number to a nushell binary primitive",
|
||||||
example: "1 | into binary",
|
example: "1 | into binary",
|
||||||
result: Some(Value::Binary {
|
result: Some(Value::Binary {
|
||||||
val: i64::from(1).to_le_bytes().to_vec(),
|
val: i64::from(1).to_ne_bytes().to_vec(),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -90,7 +92,7 @@ impl Command for SubCommand {
|
|||||||
description: "convert a boolean to a nushell binary primitive",
|
description: "convert a boolean to a nushell binary primitive",
|
||||||
example: "true | into binary",
|
example: "true | into binary",
|
||||||
result: Some(Value::Binary {
|
result: Some(Value::Binary {
|
||||||
val: i64::from(1).to_le_bytes().to_vec(),
|
val: i64::from(1).to_ne_bytes().to_vec(),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -108,7 +110,16 @@ impl Command for SubCommand {
|
|||||||
description: "convert a decimal to a nushell binary primitive",
|
description: "convert a decimal to a nushell binary primitive",
|
||||||
example: "1.234 | into binary",
|
example: "1.234 | into binary",
|
||||||
result: Some(Value::Binary {
|
result: Some(Value::Binary {
|
||||||
val: 1.234f64.to_le_bytes().to_vec(),
|
val: 1.234f64.to_ne_bytes().to_vec(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description:
|
||||||
|
"convert an integer to a nushell binary primitive with compact enabled",
|
||||||
|
example: "10 | into binary --compact",
|
||||||
|
result: Some(Value::Binary {
|
||||||
|
val: vec![10],
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -145,41 +156,28 @@ fn into_binary(
|
|||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let args = Arguments { cell_paths };
|
let args = Arguments {
|
||||||
|
cell_paths,
|
||||||
|
compact: call.has_flag("compact"),
|
||||||
|
};
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int_to_endian(n: i64) -> Vec<u8> {
|
|
||||||
if cfg!(target_endian = "little") {
|
|
||||||
n.to_le_bytes().to_vec()
|
|
||||||
} else {
|
|
||||||
n.to_be_bytes().to_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn float_to_endian(n: f64) -> Vec<u8> {
|
|
||||||
if cfg!(target_endian = "little") {
|
|
||||||
n.to_le_bytes().to_vec()
|
|
||||||
} else {
|
|
||||||
n.to_be_bytes().to_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
||||||
match input {
|
let value = match input {
|
||||||
Value::Binary { .. } => input.clone(),
|
Value::Binary { .. } => input.clone(),
|
||||||
Value::Int { val, .. } => Value::Binary {
|
Value::Int { val, .. } => Value::Binary {
|
||||||
val: int_to_endian(*val),
|
val: val.to_ne_bytes().to_vec(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Float { val, .. } => Value::Binary {
|
Value::Float { val, .. } => Value::Binary {
|
||||||
val: float_to_endian(*val),
|
val: val.to_ne_bytes().to_vec(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Filesize { val, .. } => Value::Binary {
|
Value::Filesize { val, .. } => Value::Binary {
|
||||||
val: int_to_endian(*val),
|
val: val.to_ne_bytes().to_vec(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::String { val, .. } => Value::Binary {
|
Value::String { val, .. } => Value::Binary {
|
||||||
@ -187,11 +185,11 @@ pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Bool { val, .. } => Value::Binary {
|
Value::Bool { val, .. } => Value::Binary {
|
||||||
val: int_to_endian(i64::from(*val)),
|
val: i64::from(*val).to_ne_bytes().to_vec(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Duration { val, .. } => Value::Binary {
|
Value::Duration { val, .. } => Value::Binary {
|
||||||
val: int_to_endian(*val),
|
val: val.to_ne_bytes().to_vec(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Date { val, .. } => Value::Binary {
|
Value::Date { val, .. } => Value::Binary {
|
||||||
@ -209,11 +207,38 @@ pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
src_span: other.expect_span(),
|
src_span: other.expect_span(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if _args.compact {
|
||||||
|
if let Value::Binary { val, span } = value {
|
||||||
|
let val = if cfg!(target_endian = "little") {
|
||||||
|
match val.iter().rposition(|&x| x != 0) {
|
||||||
|
Some(idx) => &val[..idx + 1],
|
||||||
|
None => &val,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match val.iter().position(|&x| x != 0) {
|
||||||
|
Some(idx) => &val[idx..],
|
||||||
|
None => &val,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::Binary {
|
||||||
|
val: val.to_vec(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -222,4 +247,26 @@ mod test {
|
|||||||
|
|
||||||
test_examples(SubCommand {})
|
test_examples(SubCommand {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(vec![10], vec![10], vec![10])]
|
||||||
|
#[case(vec![10, 0, 0], vec![10], vec![10, 0, 0])]
|
||||||
|
#[case(vec![0, 0, 10], vec![0, 0, 10], vec![10])]
|
||||||
|
#[case(vec![0, 10, 0, 0], vec![0, 10], vec![10, 0, 0])]
|
||||||
|
fn test_compact(#[case] input: Vec<u8>, #[case] little: Vec<u8>, #[case] big: Vec<u8>) {
|
||||||
|
let s = Value::test_binary(input);
|
||||||
|
let actual = action(
|
||||||
|
&s,
|
||||||
|
&Arguments {
|
||||||
|
cell_paths: None,
|
||||||
|
compact: true,
|
||||||
|
},
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
if cfg!(target_endian = "little") {
|
||||||
|
assert_eq!(actual, Value::test_binary(little));
|
||||||
|
} else {
|
||||||
|
assert_eq!(actual, Value::test_binary(big));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ impl Command for SubCommand {
|
|||||||
(Type::Date, Type::Int),
|
(Type::Date, Type::Int),
|
||||||
(Type::Duration, Type::Int),
|
(Type::Duration, Type::Int),
|
||||||
(Type::Filesize, Type::Int),
|
(Type::Filesize, Type::Int),
|
||||||
|
(Type::Binary, Type::Int),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::Table(vec![]), Type::Table(vec![])),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::Record(vec![]), Type::Record(vec![])),
|
||||||
(
|
(
|
||||||
@ -72,7 +73,12 @@ impl Command for SubCommand {
|
|||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.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)
|
.named(
|
||||||
|
"endian",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"byte encode endian, available options: native(default), little, big",
|
||||||
|
Some('e'),
|
||||||
|
)
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
@ -113,9 +119,27 @@ impl Command for SubCommand {
|
|||||||
Some(_) => 10,
|
Some(_) => 10,
|
||||||
None => 10,
|
None => 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let endian = call.get_flag::<Value>(engine_state, stack, "endian")?;
|
||||||
|
let little_endian = match endian {
|
||||||
|
Some(Value::String { val, span }) => match val.as_str() {
|
||||||
|
"native" => cfg!(target_endian = "little"),
|
||||||
|
"little" => true,
|
||||||
|
"big" => false,
|
||||||
|
_ => {
|
||||||
|
return Err(ShellError::TypeMismatch {
|
||||||
|
err_message: "Endian must be one of native, little, big".to_string(),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(_) => false,
|
||||||
|
None => cfg!(target_endian = "little"),
|
||||||
|
};
|
||||||
|
|
||||||
let args = Arguments {
|
let args = Arguments {
|
||||||
radix,
|
radix,
|
||||||
little_endian: call.has_flag("little-endian"),
|
little_endian,
|
||||||
cell_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
|
25
crates/nu-command/tests/commands/conversions/into/int.rs
Normal file
25
crates/nu-command/tests/commands/conversions/into/int.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_back_and_forth() {
|
||||||
|
let actual = nu!(r#"1 | into binary | into int"#);
|
||||||
|
assert_eq!(actual.out, "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_into_int_little_endian() {
|
||||||
|
let actual = nu!(r#"0x[01 00 00 00 00 00 00 00] | into int --endian little"#);
|
||||||
|
assert_eq!(actual.out, "1");
|
||||||
|
|
||||||
|
let actual = nu!(r#"0x[00 00 00 00 00 00 00 01] | into int --endian little"#);
|
||||||
|
assert_eq!(actual.out, "72057594037927936");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_into_int_big_endian() {
|
||||||
|
let actual = nu!(r#"0x[00 00 00 00 00 00 00 01] | into int --endian big"#);
|
||||||
|
assert_eq!(actual.out, "1");
|
||||||
|
|
||||||
|
let actual = nu!(r#"0x[01 00 00 00 00 00 00 00] | into int --endian big"#);
|
||||||
|
assert_eq!(actual.out, "72057594037927936");
|
||||||
|
}
|
1
crates/nu-command/tests/commands/conversions/into/mod.rs
Normal file
1
crates/nu-command/tests/commands/conversions/into/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod int;
|
1
crates/nu-command/tests/commands/conversions/mod.rs
Normal file
1
crates/nu-command/tests/commands/conversions/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod into;
|
@ -8,6 +8,7 @@ mod cal;
|
|||||||
mod cd;
|
mod cd;
|
||||||
mod compact;
|
mod compact;
|
||||||
mod continue_;
|
mod continue_;
|
||||||
|
mod conversions;
|
||||||
mod cp;
|
mod cp;
|
||||||
mod date;
|
mod date;
|
||||||
mod def;
|
mod def;
|
||||||
|
@ -8,7 +8,7 @@ fn binary_skip() {
|
|||||||
open sample_data.ods --raw |
|
open sample_data.ods --raw |
|
||||||
skip 2 |
|
skip 2 |
|
||||||
take 2 |
|
take 2 |
|
||||||
into int
|
into int --endian big
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user