diff --git a/crates/nu-command/src/conversions/into/bool.rs b/crates/nu-command/src/conversions/into/bool.rs index b5c443264..70dd62286 100644 --- a/crates/nu-command/src/conversions/into/bool.rs +++ b/crates/nu-command/src/conversions/into/bool.rs @@ -136,10 +136,11 @@ fn string_to_boolean(s: &str, span: Span) -> Result { let val = o.parse::(); match val { Ok(f) => Ok(f.abs() >= f64::EPSILON), - Err(_) => Err(ShellError::CantConvert( + Err(_) => Err(ShellError::CantConvertWithHelp( "boolean".to_string(), "string".to_string(), span, + r#"the strings "true" and "false" can be converted into a bool"#.to_string(), )), } } diff --git a/crates/nu-command/src/conversions/into/datetime.rs b/crates/nu-command/src/conversions/into/datetime.rs index ffca5d5c9..e0ce1ff9e 100644 --- a/crates/nu-command/src/conversions/into/datetime.rs +++ b/crates/nu-command/src/conversions/into/datetime.rs @@ -260,10 +260,11 @@ fn action( Ok(d) => Value::Date { val: d, span: head }, Err(reason) => { return Value::Error { - error: ShellError::CantConvert( + error: ShellError::CantConvertWithHelp( format!("could not parse as datetime using format '{}'", dt.0), reason.to_string(), head, + "you can use `into datetime` without a format string to enable flexible parsing".to_string() ), } } diff --git a/crates/nu-command/src/conversions/into/duration.rs b/crates/nu-command/src/conversions/into/duration.rs index ebf175a7b..f06faf0a9 100644 --- a/crates/nu-command/src/conversions/into/duration.rs +++ b/crates/nu-command/src/conversions/into/duration.rs @@ -151,10 +151,11 @@ fn string_to_duration(s: &str, span: Span) -> Result { } } - Err(ShellError::CantConvert( + Err(ShellError::CantConvertWithHelp( "duration".to_string(), "string".to_string(), span, + "supported units are ns, us, ms, sec, min, hr, day, and wk".to_string(), )) } diff --git a/crates/nu-command/src/conversions/into/int.rs b/crates/nu-command/src/conversions/into/int.rs index d8c211f18..14c70aac1 100644 --- a/crates/nu-command/src/conversions/into/int.rs +++ b/crates/nu-command/src/conversions/into/int.rs @@ -206,8 +206,8 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value { }; match i64::from_str_radix(&i, radix) { Ok(n) => Value::Int { val: n, span: head }, - Err(reason) => Value::Error { - error: ShellError::CantConvert("".to_string(), reason.to_string(), head), + Err(_reason) => Value::Error { + error: ShellError::CantConvert("int".to_string(), "string".to_string(), head), }, } } @@ -218,11 +218,12 @@ fn int_from_string(a_string: &str, span: Span) -> Result { b if b.starts_with("0b") => { let num = match i64::from_str_radix(b.trim_start_matches("0b"), 2) { Ok(n) => n, - Err(reason) => { - return Err(ShellError::CantConvert( - "could not parse as integer".to_string(), - reason.to_string(), + Err(_reason) => { + return Err(ShellError::CantConvertWithHelp( + "int".to_string(), + "string".to_string(), span, + r#"digits following "0b" can only be 0 or 1"#.to_string(), )) } }; @@ -231,11 +232,13 @@ fn int_from_string(a_string: &str, span: Span) -> Result { h if h.starts_with("0x") => { let num = match i64::from_str_radix(h.trim_start_matches("0x"), 16) { Ok(n) => n, - Err(reason) => { - return Err(ShellError::CantConvert( - "could not parse as int".to_string(), - reason.to_string(), + Err(_reason) => { + return Err(ShellError::CantConvertWithHelp( + "int".to_string(), + "string".to_string(), span, + r#"hexadecimal digits following "0x" should be in 0-9, a-f, or A-F"# + .to_string(), )) } }; @@ -246,7 +249,7 @@ fn int_from_string(a_string: &str, span: Span) -> Result { Err(_) => match a_string.parse::() { Ok(f) => Ok(f as i64), _ => Err(ShellError::CantConvert( - "into int".to_string(), + "int".to_string(), "string".to_string(), span, )), diff --git a/crates/nu-command/src/conversions/into/string.rs b/crates/nu-command/src/conversions/into/string.rs index 298a072a8..eb36d2dc2 100644 --- a/crates/nu-command/src/conversions/into/string.rs +++ b/crates/nu-command/src/conversions/into/string.rs @@ -257,6 +257,14 @@ pub fn action( span, ), }, + Value::Binary { .. } => Value::Error { + error: ShellError::CantConvertWithHelp( + "string".into(), + "binary".into(), + span, + "try using the `decode` command".into(), + ), + }, x => Value::Error { error: ShellError::CantConvert(String::from("string"), x.get_type().to_string(), span), }, diff --git a/crates/nu-command/src/formats/from/eml.rs b/crates/nu-command/src/formats/from/eml.rs index 36a1a94a0..c0170a40b 100644 --- a/crates/nu-command/src/formats/from/eml.rs +++ b/crates/nu-command/src/formats/from/eml.rs @@ -193,7 +193,7 @@ fn from_eml( .with_body_preview(body_preview) .parse() .map_err(|_| { - ShellError::CantConvert("structured data from eml".into(), "string".into(), head) + ShellError::CantConvert("structured eml data".into(), "string".into(), head) })?; let mut collected = IndexMap::new(); diff --git a/crates/nu-command/src/formats/from/json.rs b/crates/nu-command/src/formats/from/json.rs index 58d6bf411..a16104a0d 100644 --- a/crates/nu-command/src/formats/from/json.rs +++ b/crates/nu-command/src/formats/from/json.rs @@ -135,7 +135,7 @@ fn convert_nujson_to_value(value: &nu_json::Value, span: Span) -> Value { Value::Error { error: ShellError::CantConvert( "i64 sized integer".into(), - "larger than i64".into(), + "value larger than i64".into(), span, ), } @@ -201,7 +201,7 @@ fn convert_string_to_value(string_input: String, span: Span) -> Result Err(ShellError::CantConvert( - format!("structured data from json ({})", x), + format!("structured json data ({})", x), "string".into(), span, )), diff --git a/crates/nu-command/src/formats/from/toml.rs b/crates/nu-command/src/formats/from/toml.rs index 477b2c3ec..1d0ac6396 100644 --- a/crates/nu-command/src/formats/from/toml.rs +++ b/crates/nu-command/src/formats/from/toml.rs @@ -121,7 +121,7 @@ pub fn convert_string_to_value(string_input: String, span: Span) -> Result Ok(convert_toml_to_value(&value, span)), Err(_x) => Err(ShellError::CantConvert( - "structured data from toml".into(), + "structured toml data".into(), "string".into(), span, )), diff --git a/crates/nu-plugin/src/protocol/mod.rs b/crates/nu-plugin/src/protocol/mod.rs index ace953a99..4a92a11a3 100644 --- a/crates/nu-plugin/src/protocol/mod.rs +++ b/crates/nu-plugin/src/protocol/mod.rs @@ -47,7 +47,8 @@ impl From for LabeledError { msg, span: None, }, - ShellError::CantConvert(expected, input, span) => LabeledError { + ShellError::CantConvert(expected, input, span) + | ShellError::CantConvertWithHelp(expected, input, span, _) => LabeledError { label: format!("Can't convert to {}", expected), msg: format!("can't convert {} to {}", expected, input), span: Some(span), diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 7851a0bdf..b80cc30cf 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -112,6 +112,16 @@ pub enum ShellError { #[diagnostic(code(nu::shell::cant_convert), url(docsrs))] CantConvert(String, String, #[label("can't convert {1} to {0}")] Span), + // Identical to above, but with help + #[error("Can't convert to {0}.")] + #[diagnostic(code(nu::shell::cant_convert), url(docsrs), help("{3}"))] + CantConvertWithHelp( + String, + String, + #[label("can't convert {1} to {0}")] Span, + String, + ), + #[error("{0} is not representable as a string.")] #[diagnostic( code(nu::shell::env_var_not_a_string), diff --git a/crates/nu-protocol/src/value/from.rs b/crates/nu-protocol/src/value/from.rs index ea3ff0ef7..c56523f6c 100644 --- a/crates/nu-protocol/src/value/from.rs +++ b/crates/nu-protocol/src/value/from.rs @@ -16,7 +16,7 @@ impl Value { match self { Value::Int { val, .. } => Ok(*val), x => Err(ShellError::CantConvert( - "rf64".into(), + "i64".into(), x.get_type().to_string(), self.span()?, )),