mirror of
https://github.com/nushell/nushell.git
synced 2025-05-29 14:21:45 +02:00
# Description This PR tights input/output type-checking a bit more. There are a lot of commands that don't have correct input/output types, so part of the effort is updating them. This PR now contains updates to commands that had wrong input/output signatures. It doesn't add examples for these new signatures, but that can be follow-up work. # User-Facing Changes BREAKING CHANGE BREAKING CHANGE This work enforces many more checks on pipeline type correctness than previous nushell versions. This strictness may uncover incompatibilities in existing scripts or shortcomings in the type information for internal commands. # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass - `cargo run -- -c "use std testing; testing run-tests --path crates/nu-std"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
171 lines
5.6 KiB
Rust
171 lines
5.6 KiB
Rust
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
|
use nu_engine::CallExt;
|
|
use nu_protocol::ast::Call;
|
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
use nu_protocol::{
|
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
|
};
|
|
use num_traits::int::PrimInt;
|
|
use std::fmt::Display;
|
|
|
|
#[derive(Clone)]
|
|
pub struct BitsRor;
|
|
|
|
impl Command for BitsRor {
|
|
fn name(&self) -> &str {
|
|
"bits ror"
|
|
}
|
|
|
|
fn signature(&self) -> Signature {
|
|
Signature::build("bits ror")
|
|
.input_output_types(vec![
|
|
(Type::Int, Type::Int),
|
|
(
|
|
Type::List(Box::new(Type::Int)),
|
|
Type::List(Box::new(Type::Int)),
|
|
),
|
|
])
|
|
.vectorizes_over_list(true)
|
|
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
|
|
.switch(
|
|
"signed",
|
|
"always treat input number as a signed number",
|
|
Some('s'),
|
|
)
|
|
.named(
|
|
"number-bytes",
|
|
SyntaxShape::String,
|
|
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
|
|
Some('n'),
|
|
)
|
|
.category(Category::Bits)
|
|
}
|
|
|
|
fn usage(&self) -> &str {
|
|
"Bitwise rotate right for integers."
|
|
}
|
|
|
|
fn search_terms(&self) -> Vec<&str> {
|
|
vec!["rotate right"]
|
|
}
|
|
|
|
fn run(
|
|
&self,
|
|
engine_state: &EngineState,
|
|
stack: &mut Stack,
|
|
call: &Call,
|
|
input: PipelineData,
|
|
) -> Result<PipelineData, ShellError> {
|
|
let head = call.head;
|
|
let bits: usize = call.req(engine_state, stack, 0)?;
|
|
let signed = call.has_flag("signed");
|
|
let number_bytes: Option<Spanned<String>> =
|
|
call.get_flag(engine_state, stack, "number-bytes")?;
|
|
let bytes_len = get_number_bytes(&number_bytes);
|
|
if let NumberBytes::Invalid = bytes_len {
|
|
if let Some(val) = number_bytes {
|
|
return Err(ShellError::UnsupportedInput(
|
|
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
|
"value originates from here".to_string(),
|
|
head,
|
|
val.span,
|
|
));
|
|
}
|
|
}
|
|
// This doesn't match explicit nulls
|
|
if matches!(input, PipelineData::Empty) {
|
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
|
}
|
|
input.map(
|
|
move |value| operate(value, bits, head, signed, bytes_len),
|
|
engine_state.ctrlc.clone(),
|
|
)
|
|
}
|
|
|
|
fn examples(&self) -> Vec<Example> {
|
|
vec![
|
|
Example {
|
|
description: "Rotate right a number with 60 bits",
|
|
example: "17 | bits ror 60",
|
|
result: Some(Value::test_int(272)),
|
|
},
|
|
Example {
|
|
description: "Rotate right a list of numbers of one byte",
|
|
example: "[15 33 92] | bits ror 2 -n '1'",
|
|
result: Some(Value::List {
|
|
vals: vec![
|
|
Value::test_int(195),
|
|
Value::test_int(72),
|
|
Value::test_int(23),
|
|
],
|
|
span: Span::test_data(),
|
|
}),
|
|
},
|
|
]
|
|
}
|
|
}
|
|
|
|
fn get_rotate_right<T: Display + PrimInt>(val: T, bits: u32, span: Span) -> Value
|
|
where
|
|
i64: std::convert::TryFrom<T>,
|
|
{
|
|
let rotate_result = i64::try_from(val.rotate_right(bits));
|
|
match rotate_result {
|
|
Ok(val) => Value::Int { val, span },
|
|
Err(_) => Value::Error {
|
|
error: Box::new(ShellError::GenericError(
|
|
"Rotate right result beyond the range of 64 bit signed number".to_string(),
|
|
format!(
|
|
"{val} of the specified number of bytes rotate right {bits} bits exceed limit"
|
|
),
|
|
Some(span),
|
|
None,
|
|
Vec::new(),
|
|
)),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
|
|
match value {
|
|
Value::Int { val, span } => {
|
|
use InputNumType::*;
|
|
// let bits = (((bits % 64) + 64) % 64) as u32;
|
|
let bits = bits as u32;
|
|
let input_type = get_input_num_type(val, signed, number_size);
|
|
match input_type {
|
|
One => get_rotate_right(val as u8, bits, span),
|
|
Two => get_rotate_right(val as u16, bits, span),
|
|
Four => get_rotate_right(val as u32, bits, span),
|
|
Eight => get_rotate_right(val as u64, bits, span),
|
|
SignedOne => get_rotate_right(val as i8, bits, span),
|
|
SignedTwo => get_rotate_right(val as i16, bits, span),
|
|
SignedFour => get_rotate_right(val as i32, bits, span),
|
|
SignedEight => get_rotate_right(val, bits, span),
|
|
}
|
|
}
|
|
// Propagate errors by explicitly matching them before the final case.
|
|
Value::Error { .. } => value,
|
|
other => Value::Error {
|
|
error: Box::new(ShellError::OnlySupportsThisInputType {
|
|
exp_input_type: "integer".into(),
|
|
wrong_type: other.get_type().to_string(),
|
|
dst_span: head,
|
|
src_span: other.expect_span(),
|
|
}),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_examples() {
|
|
use crate::test_examples;
|
|
|
|
test_examples(BitsRor {})
|
|
}
|
|
}
|