Replace IncorrectValue with InvalidValue

This commit is contained in:
Ian Manske 2024-09-29 20:47:00 -07:00
parent 1f47d72e86
commit 9a2a86399c
18 changed files with 183 additions and 208 deletions

View File

@ -130,13 +130,13 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
if bits > input_num_type.num_bits() {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to rotate by more than the available bits ({})",
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
input_num_type.num_bits()
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);
@ -171,16 +171,16 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Value::int(int, span)
}
Value::Binary { val, .. } => {
let len = val.len();
if bits > len * 8 {
let max_bits = val.len() * 8;
if bits > max_bits {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to rotate by more than the available bits ({})",
len * 8
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
max_bits
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);

View File

@ -134,13 +134,13 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
if bits > input_num_type.num_bits() {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to rotate by more than the available bits ({})",
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
input_num_type.num_bits()
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);
@ -175,16 +175,16 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Value::int(int, span)
}
Value::Binary { val, .. } => {
let len = val.len();
if bits > len * 8 {
let max_bits = val.len() * 8;
if bits > max_bits {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to rotate by more than the available bits ({})",
len * 8
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
max_bits
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);

View File

@ -145,13 +145,13 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let input_num_type = get_input_num_type(val, signed, number_size);
if !input_num_type.is_permitted_bit_shift(bits) {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to shift by more than the available bits (permitted < {})",
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
input_num_type.num_bits()
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);
@ -188,24 +188,25 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Value::int(int, span)
}
Value::Binary { val, .. } => {
let byte_shift = bits / 8;
let bit_shift = bits % 8;
// This is purely for symmetry with the int case and the fact that the
// shift right implementation in its current form panicked with an overflow
if bits > val.len() * 8 {
let max_bits = val.len() * 8;
if bits > max_bits {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to shift by more than the available bits ({})",
val.len() * 8
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
max_bits
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);
}
let byte_shift = bits / 8;
let bit_shift = bits % 8;
let bytes = if bit_shift == 0 {
shift_bytes_left(val, byte_shift)
} else {

View File

@ -132,13 +132,13 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
if !input_num_type.is_permitted_bit_shift(bits) {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to shift by more than the available bits (permitted < {})",
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
input_num_type.num_bits()
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);
@ -157,26 +157,26 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Value::int(int, span)
}
Value::Binary { val, .. } => {
let byte_shift = bits / 8;
let bit_shift = bits % 8;
let len = val.len();
// This check is done for symmetry with the int case and the previous
// implementation would overflow byte indices leading to unexpected output
// lengths
if bits > len * 8 {
let max_bits = val.len() * 8;
if bits > max_bits {
return Value::error(
ShellError::IncorrectValue {
msg: format!(
"Trying to shift by more than the available bits ({})",
len * 8
ShellError::InvalidValue {
valid: format!(
"an integer less than or equal to the available number of bits ({})",
max_bits
),
val_span: bits_span,
call_span: span,
actual: bits.to_string(),
span: bits_span,
},
span,
);
}
let byte_shift = bits / 8;
let bit_shift = bits % 8;
let bytes = if bit_shift == 0 {
shift_bytes_right(val, byte_shift)
} else {

View File

@ -55,10 +55,10 @@ impl Command for BytesBuild {
match val {
Value::Binary { mut val, .. } => output.append(&mut val),
Value::Int { val, .. } => {
let byte: u8 = val.try_into().map_err(|_| ShellError::IncorrectValue {
msg: format!("{val} is out of range for byte"),
val_span,
call_span: call.head,
let byte: u8 = val.try_into().map_err(|_| ShellError::InvalidValue {
valid: "an integer in the range [0, 255]".into(),
actual: val.to_string(),
span: val_span,
})?;
output.push(byte);
}

View File

@ -1,7 +1,7 @@
use chrono::{FixedOffset, TimeZone};
use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*;
use nu_protocol::IntoValue;
use nu_utils::get_system_locale;
struct Arguments {
@ -294,30 +294,21 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Value::int(0, span)
}
}
Value::Date { val, .. } => {
if val
< &FixedOffset::east_opt(0)
.expect("constant")
.with_ymd_and_hms(1677, 9, 21, 0, 12, 44)
.unwrap()
|| val
> &FixedOffset::east_opt(0)
.expect("constant")
.with_ymd_and_hms(2262, 4, 11, 23, 47, 16)
.unwrap()
{
Value::error (
ShellError::IncorrectValue {
msg: "DateTime out of range for timestamp: 1677-09-21T00:12:43Z to 2262-04-11T23:47:16".to_string(),
val_span,
call_span: span,
Value::Date { val, .. } => val
.timestamp_nanos_opt()
.map(|nanos| nanos.into_value(span))
.unwrap_or_else(|| {
Value::error(
ShellError::GenericError {
error: "Date value out of range".into(),
msg: "cannot fit this date's number of nanoseconds into an int".into(),
span: Some(span),
help: None,
inner: Vec::new(),
},
span,
)
} else {
Value::int(val.timestamp_nanos_opt().unwrap_or_default(), span)
}
}
}),
Value::Duration { val, .. } => Value::int(*val, span),
Value::Binary { val, .. } => {
use byteorder::{BigEndian, ByteOrder, LittleEndian};
@ -331,10 +322,10 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
if size > 8 {
return Value::error(
ShellError::IncorrectValue {
msg: format!("binary input is too large to convert to int ({size} bytes)"),
val_span,
call_span: span,
ShellError::InvalidValue {
valid: "a binary value with 8 or less bytes".into(),
actual: format!("one with {size} bytes"),
span: val_span,
},
span,
);
@ -617,8 +608,8 @@ mod test {
}
#[rstest]
#[case("2262-04-11T23:47:17+00:00", "DateTime out of range for timestamp")]
#[case("1677-09-21T00:12:43+00:00", "DateTime out of range for timestamp")]
#[case("2262-04-11T23:47:17+00:00", "Date value out of range")]
#[case("1677-09-21T00:12:43+00:00", "Date value out of range")]
fn datetime_to_int_values_that_fail(
#[case] dt_in: DateTime<FixedOffset>,
#[case] err_expected: &str,
@ -635,7 +626,7 @@ mod test {
Span::test_data(),
);
if let Value::Error { error, .. } = actual {
if let ShellError::IncorrectValue { msg: e, .. } = *error {
if let ShellError::GenericError { error: e, .. } = *error {
assert!(
e.contains(err_expected),
"{e:?} doesn't contain {err_expected}"

View File

@ -141,13 +141,10 @@ fn into_record(call: &Call, input: PipelineData) -> Result<PipelineData, ShellEr
let (val, key) = vals.pop().zip(vals.pop()).expect("length is < 2");
record.insert(key.coerce_into_string()?, val);
} else {
return Err(ShellError::IncorrectValue {
msg: format!(
"expected inner list with two elements, but found {} element(s)",
vals.len()
),
val_span: span,
call_span: call.head,
return Err(ShellError::InvalidValue {
valid: "a list with 2 elements".into(),
actual: format!("a list with {} element(s)", vals.len()),
span,
});
}
expected_type = Some(ExpectedType::Pair);

View File

@ -84,17 +84,23 @@ the declaration may not be in scope.
}
// Decl by ID - IR dump always shows name of decl, but sometimes it isn't in scope
Value::Int { val, .. } if is_decl_id => {
let decl_id = val
.try_into()
.ok()
.map(DeclId::new)
.filter(|id| id.get() < engine_state.num_decls())
.ok_or_else(|| ShellError::IncorrectValue {
msg: "not a valid decl id".into(),
val_span: target.span(),
call_span: call.head,
let decl_id = val.try_into().map_err(|_| ShellError::InvalidValue {
valid: "a non-negative integer".into(),
actual: val.to_string(),
span: target.span(),
})?;
let decl = engine_state.get_decl(decl_id);
if decl_id >= engine_state.num_decls() {
return Err(ShellError::GenericError {
error: format!("Unknown decl ID: {decl_id}"),
msg: "ensure the decl ID is correct and try again".into(),
span: Some(target.span()),
help: None,
inner: vec![],
});
};
let decl = engine_state.get_decl(DeclId::new(decl_id));
decl.block_id().ok_or_else(|| ShellError::GenericError {
error: format!("Can't view IR for `{}`", decl.name()),
msg: "not a custom command".into(),
@ -107,10 +113,10 @@ the declaration may not be in scope.
Value::Int { val, .. } => {
val.try_into()
.map(BlockId::new)
.map_err(|_| ShellError::IncorrectValue {
msg: "not a valid block id".into(),
val_span: target.span(),
call_span: call.head,
.map_err(|_| ShellError::InvalidValue {
valid: "a non-negative interger".into(),
actual: val.to_string(),
span: target.span(),
})?
}
// Pass through errors

View File

@ -148,15 +148,15 @@ impl Command for Glob {
}
};
let glob_pattern =
match glob_pattern_input {
let glob_pattern = match glob_pattern_input {
Value::String { val, .. } | Value::Glob { val, .. } => val,
_ => return Err(ShellError::IncorrectValue {
msg: "Incorrect glob pattern supplied to glob. Please use string or glob only."
.to_string(),
val_span: call.head,
call_span: glob_span,
}),
val => {
return Err(ShellError::RuntimeTypeMismatch {
expected: Type::custom("string or glob"),
actual: val.get_type(),
span: val.span(),
});
}
};
if glob_pattern.is_empty() {

View File

@ -83,17 +83,14 @@ impl Command for Chunks {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let chunk_size: Value = call.req(engine_state, stack, 0)?;
let chunk_size: Spanned<i64> = call.req(engine_state, stack, 0)?;
let size =
usize::try_from(chunk_size.as_int()?).map_err(|_| ShellError::NeedsPositiveValue {
span: chunk_size.span(),
})?;
let size = NonZeroUsize::try_from(size).map_err(|_| ShellError::IncorrectValue {
msg: "`chunk_size` cannot be zero".into(),
val_span: chunk_size.span(),
call_span: head,
let size = usize::try_from(chunk_size.item)
.and_then(NonZeroUsize::try_from)
.map_err(|_| ShellError::InvalidValue {
valid: "an integer greater than 0".into(),
actual: chunk_size.item.to_string(),
span: chunk_size.span,
})?;
chunks(engine_state, input, size, head)

View File

@ -82,32 +82,25 @@ impl Command for Window {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let window_size: Value = call.req(engine_state, stack, 0)?;
let stride: Option<Value> = call.get_flag(engine_state, stack, "stride")?;
let window_size: Spanned<i64> = call.req(engine_state, stack, 0)?;
let stride: Option<Spanned<i64>> = call.get_flag(engine_state, stack, "stride")?;
let remainder = call.has_flag(engine_state, stack, "remainder")?;
let size =
usize::try_from(window_size.as_int()?).map_err(|_| ShellError::NeedsPositiveValue {
span: window_size.span(),
let size = usize::try_from(window_size.item)
.and_then(NonZeroUsize::try_from)
.map_err(|_| ShellError::InvalidValue {
valid: "an integer greater than 0".into(),
actual: window_size.item.to_string(),
span: window_size.span,
})?;
let size = NonZeroUsize::try_from(size).map_err(|_| ShellError::IncorrectValue {
msg: "`window_size` cannot be zero".into(),
val_span: window_size.span(),
call_span: head,
})?;
let stride = if let Some(stride_val) = stride {
let stride = usize::try_from(stride_val.as_int()?).map_err(|_| {
ShellError::NeedsPositiveValue {
span: stride_val.span(),
}
})?;
NonZeroUsize::try_from(stride).map_err(|_| ShellError::IncorrectValue {
msg: "`stride` cannot be zero".into(),
val_span: stride_val.span(),
call_span: head,
let stride = if let Some(stride) = stride {
usize::try_from(stride.item)
.and_then(NonZeroUsize::try_from)
.map_err(|_| ShellError::InvalidValue {
valid: "an integer greater than 0".into(),
actual: stride.item.to_string(),
span: stride.span,
})?
} else {
NonZeroUsize::MIN

View File

@ -100,10 +100,10 @@ pub fn to_delimited_data(
.with_content_type(content_type),
);
let separator = u8::try_from(separator.item).map_err(|_| ShellError::IncorrectValue {
msg: "separator must be an ASCII character".into(),
val_span: separator.span,
call_span: head,
let separator = u8::try_from(separator.item).map_err(|_| ShellError::InvalidValue {
valid: "an ASCII character".into(),
actual: separator.item.to_string(),
span: separator.span,
})?;
// Check to ensure the input is likely one of our supported types first. We can't check a stream

View File

@ -313,11 +313,13 @@ fn send_form_request(
match body {
Value::List { ref vals, .. } => {
if vals.len() % 2 != 0 {
return Err(ShellErrorOrRequestError::ShellError(ShellError::IncorrectValue {
msg: "Body type 'list' for form requests requires paired values. E.g.: [foo, 10]".into(),
val_span: body.span(),
call_span: span,
}));
return Err(ShellErrorOrRequestError::ShellError(
ShellError::InvalidValue {
valid: "a list with an even number of elements".into(),
actual: format!("a list with {} elements", vals.len()),
span: body.span(),
},
));
}
let data = vals

View File

@ -338,10 +338,9 @@ fn set_limits(
res: &ResourceInfo,
soft: bool,
hard: bool,
call_span: Span,
) -> Result<(), ShellError> {
let (mut soft_limit, mut hard_limit) = getrlimit(res.resource)?;
let new_limit = parse_limit(limit_value, res, soft, soft_limit, hard_limit, call_span)?;
let new_limit = parse_limit(limit_value, res, soft, soft_limit, hard_limit)?;
if hard {
hard_limit = new_limit;
@ -429,14 +428,14 @@ fn parse_limit(
soft: bool,
soft_limit: rlim_t,
hard_limit: rlim_t,
call_span: Span,
) -> Result<rlim_t, ShellError> {
let span = limit_value.span();
match limit_value {
Value::Int { val, internal_span } => {
Value::Int { val, .. } => {
let value = rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
to_type: "rlim_t".into(),
from_type: "i64".into(),
span: *internal_span,
span,
help: Some(e.to_string()),
})?;
@ -447,25 +446,25 @@ fn parse_limit(
Ok(limit)
}
}
Value::Filesize { val, internal_span } => {
Value::Filesize { val, .. } => {
if res.multiplier != 1024 {
return Err(ShellError::TypeMismatch {
err_message: format!(
"filesize is not compatible with resource {:?}",
res.resource
),
span: *internal_span,
span,
});
}
rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
to_type: "rlim_t".into(),
from_type: "i64".into(),
span: *internal_span,
span,
help: Some(e.to_string()),
})
}
Value::String { val, internal_span } => {
Value::String { val, .. } => {
if val == "unlimited" {
Ok(RLIM_INFINITY)
} else if val == "soft" {
@ -477,10 +476,10 @@ fn parse_limit(
} else if val == "hard" {
Ok(hard_limit)
} else {
return Err(ShellError::IncorrectValue {
msg: "Only unlimited, soft and hard are supported for strings".into(),
val_span: *internal_span,
call_span,
return Err(ShellError::InvalidValue {
valid: "'unlimited', 'soft', or 'hard'".into(),
actual: format!("'{val}'"),
span,
});
}
}
@ -489,7 +488,7 @@ fn parse_limit(
"string, int or filesize required, you provide {}",
limit_value.get_type()
),
span: limit_value.span(),
span,
}),
}
}
@ -544,7 +543,7 @@ impl Command for ULimit {
for res in RESOURCE_ARRAY.iter() {
if call.has_flag(engine_state, stack, res.name)? {
set_limits(&limit_value, res, soft, hard, call.head)?;
set_limits(&limit_value, res, soft, hard)?;
if set_default_limit {
set_default_limit = false;
@ -555,7 +554,7 @@ impl Command for ULimit {
// Set `RLIMIT_FSIZE` limit if no resource flag provided.
if set_default_limit {
let res = ResourceInfo::default();
set_limits(&limit_value, &res, hard, soft, call.head)?;
set_limits(&limit_value, &res, hard, soft)?;
}
Ok(PipelineData::Empty)

View File

@ -24,10 +24,13 @@ pub fn decode(
let output = match encoding.decode(input_str.as_bytes()) {
Ok(output) => output,
Err(err) => {
return Err(ShellError::IncorrectValue {
// TODO: convert/map each possible `DecodeError` case.
return Err(ShellError::GenericError {
error: "Failed to decode".into(),
msg: err.to_string(),
val_span: input_span,
call_span,
span: Some(call_span),
help: None,
inner: Vec::new(),
});
}
};

View File

@ -249,9 +249,8 @@ fn action(
false => "",
};
let regex_string = flags.to_string() + find_str;
let regex = Regex::new(&regex_string);
match regex {
match Regex::new(&regex_string) {
Ok(re) => {
if *all {
Value::string(
@ -278,10 +277,12 @@ fn action(
}
}
Err(e) => Value::error(
ShellError::IncorrectValue {
msg: format!("Regex error: {e}"),
val_span: find.span,
call_span: head,
ShellError::GenericError {
error: "Failed to create regex".into(),
msg: e.to_string(),
span: Some(find.span),
help: None,
inner: Vec::new(),
},
find.span,
),

View File

@ -135,21 +135,6 @@ pub enum ShellError {
span: Span,
},
/// A command received an argument with correct type but incorrect value.
///
/// ## Resolution
///
/// Correct the argument value before passing it in or change the command.
#[error("Incorrect value.")]
#[diagnostic(code(nu::shell::incorrect_value))]
IncorrectValue {
msg: String,
#[label = "{msg}"]
val_span: Span,
#[label = "encountered here"]
call_span: Span,
},
/// This value cannot be used with this operator.
///
/// ## Resolution

View File

@ -17,10 +17,10 @@ fn get_compression(call: &EvaluatedCall) -> Result<Option<AvroCompression>, Shel
match compression.as_ref() {
"snappy" => Ok(Some(AvroCompression::Snappy)),
"deflate" => Ok(Some(AvroCompression::Deflate)),
_ => Err(ShellError::IncorrectValue {
msg: "compression must be one of deflate or snappy".to_string(),
val_span: span,
call_span: span,
_ => Err(ShellError::InvalidValue {
valid: "'deflate' or 'snappy'".into(),
actual: format!("'{compression}'"),
span,
}),
}
} else {