diff --git a/Cargo.lock b/Cargo.lock index 7ab87af86c..696b84d35e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,6 +596,7 @@ dependencies = [ name = "nu-engine" version = "0.1.0" dependencies = [ + "chrono", "itertools", "nu-parser", "nu-path", diff --git a/crates/nu-command/src/conversions/into/int.rs b/crates/nu-command/src/conversions/into/int.rs index 491379c70c..f53aee0833 100644 --- a/crates/nu-command/src/conversions/into/int.rs +++ b/crates/nu-command/src/conversions/into/int.rs @@ -55,26 +55,17 @@ impl Command for SubCommand { Example { description: "Convert string to integer", example: "'2' | into int", - result: Some(Value::Int { - val: 2, - span: Span::unknown(), - }), + result: Some(Value::test_int(2)), }, Example { description: "Convert decimal to integer", example: "5.9 | into int", - result: Some(Value::Int { - val: 5, - span: Span::unknown(), - }), + result: Some(Value::test_int(5)), }, Example { description: "Convert decimal string to integer", example: "'5.9' | into int", - result: Some(Value::Int { - val: 5, - span: Span::unknown(), - }), + result: Some(Value::test_int(5)), }, Example { description: "Convert file size to integer", @@ -88,18 +79,9 @@ impl Command for SubCommand { description: "Convert bool to integer", example: "[$false, $true] | into int", result: Some(Value::Stream { - stream: vec![ - Value::Int { - val: 0, - span: Span::unknown(), - }, - Value::Int { - val: 1, - span: Span::unknown(), - }, - ] - .into_iter() - .into_value_stream(), + stream: vec![Value::test_int(0), Value::test_int(1)] + .into_iter() + .into_value_stream(), span: Span::unknown(), }), }, diff --git a/crates/nu-command/src/experimental/git.rs b/crates/nu-command/src/experimental/git.rs index eddb042bc7..cef4a27db0 100644 --- a/crates/nu-command/src/experimental/git.rs +++ b/crates/nu-command/src/experimental/git.rs @@ -34,7 +34,10 @@ impl Command for Git { Ok(val) => { let result = val.stdout; - Ok(Value::string(&String::from_utf8_lossy(&result), call.head)) + Ok(Value::String { + val: String::from_utf8_lossy(&result).to_string(), + span: call.head, + }) } Err(_err) => { // FIXME: Move this to an external signature and add better error handling diff --git a/crates/nu-command/src/experimental/git_checkout.rs b/crates/nu-command/src/experimental/git_checkout.rs index 84cd0e5d33..d25d607d7b 100644 --- a/crates/nu-command/src/experimental/git_checkout.rs +++ b/crates/nu-command/src/experimental/git_checkout.rs @@ -49,7 +49,10 @@ impl Command for GitCheckout { Ok(val) => { let result = val.stdout; - Ok(Value::string(&String::from_utf8_lossy(&result), call.head)) + Ok(Value::String { + val: String::from_utf8_lossy(&result).to_string(), + span: call.head, + }) } Err(_err) => { // FIXME: Move this to an external signature and add better error handling diff --git a/crates/nu-command/src/filters/lines.rs b/crates/nu-command/src/filters/lines.rs index a6dd017a87..2a32b54ca6 100644 --- a/crates/nu-command/src/filters/lines.rs +++ b/crates/nu-command/src/filters/lines.rs @@ -42,7 +42,7 @@ impl Command for Lines { let iter = lines.into_iter().filter_map(move |s| { if !s.is_empty() { - Some(Value::String { val: s, span }) + Some(Value::string(s, span)) } else { None } diff --git a/crates/nu-command/src/strings/split/chars.rs b/crates/nu-command/src/strings/split/chars.rs index b326c78642..c74dce2f17 100644 --- a/crates/nu-command/src/strings/split/chars.rs +++ b/crates/nu-command/src/strings/split/chars.rs @@ -25,26 +25,11 @@ impl Command for SubCommand { example: "'hello' | split chars", result: Some(Value::List { vals: vec![ - Value::String { - val: "h".into(), - span: Span::unknown(), - }, - Value::String { - val: "e".into(), - span: Span::unknown(), - }, - Value::String { - val: "l".into(), - span: Span::unknown(), - }, - Value::String { - val: "l".into(), - span: Span::unknown(), - }, - Value::String { - val: "o".into(), - span: Span::unknown(), - }, + Value::test_string("h"), + Value::test_string("e"), + Value::test_string("l"), + Value::test_string("l"), + Value::test_string("o"), ], span: Span::unknown(), }), @@ -74,10 +59,7 @@ fn split_chars_helper(v: &Value, name: Span) -> Vec { s.chars() .collect::>() .into_iter() - .map(move |x| Value::String { - val: x.to_string(), - span: v_span, - }) + .map(move |x| Value::string(x, v_span)) .collect() } else { vec![Value::Error { diff --git a/crates/nu-command/src/strings/split/column.rs b/crates/nu-command/src/strings/split/column.rs index c0a8e03bbd..ace779bd20 100644 --- a/crates/nu-command/src/strings/split/column.rs +++ b/crates/nu-command/src/strings/split/column.rs @@ -86,18 +86,12 @@ fn split_column_helper( for (&k, v) in split_result.iter().zip(&gen_columns) { cols.push(v.to_string()); - vals.push(Value::String { - val: k.into(), - span: head, - }); + vals.push(Value::string(k, head)); } } else { for (&k, v) in split_result.iter().zip(&positional) { cols.push(v.into()); - vals.push(Value::String { - val: k.into(), - span: head, - }) + vals.push(Value::string(k, head)); } } Value::List { diff --git a/crates/nu-command/src/strings/split/row.rs b/crates/nu-command/src/strings/split/row.rs index 7f8c49a4b2..afda969770 100644 --- a/crates/nu-command/src/strings/split/row.rs +++ b/crates/nu-command/src/strings/split/row.rs @@ -55,10 +55,7 @@ fn split_row_helper(v: &Value, separator: &Spanned, name: Span) -> Vec { } } -// impl FromValue for DateTime { -// fn from_value(v: &Value) -> Result { -// match v { -// Value { -// value: UntaggedValue::Primitive(Primitive::Date(d)), -// .. -// } => Ok(*d), -// Value { -// value: UntaggedValue::Row(_), -// .. -// } => { -// let mut shell_error = ShellError::type_error("date", v.spanned_type_name()); -// shell_error.notes.push( -// "Note: you can access columns using dot. eg) $it.column or (ls).column".into(), -// ); -// Err(shell_error) -// } -// v => Err(ShellError::type_error("date", v.spanned_type_name())), -// } -// } -// } +impl FromValue for DateTime { + fn from_value(v: &Value) -> Result { + match v { + Value::Date { val, .. } => Ok(*val), + v => Err(ShellError::CantConvert("date".into(), v.span()?)), + } + } +} + +impl FromValue for Spanned> { + fn from_value(v: &Value) -> Result { + match v { + Value::Date { val, span } => Ok(Spanned { + item: *val, + span: *span, + }), + v => Err(ShellError::CantConvert("date".into(), v.span()?)), + } + } +} impl FromValue for Range { fn from_value(v: &Value) -> Result { @@ -177,31 +177,15 @@ impl FromValue for Spanned { } } -// impl FromValue for Vec { -// fn from_value(v: &Value) -> Result { -// match v { -// Value { -// value: UntaggedValue::Primitive(Primitive::Binary(b)), -// .. -// } => Ok(b.clone()), -// Value { -// value: UntaggedValue::Primitive(Primitive::String(s)), -// .. -// } => Ok(s.bytes().collect()), -// Value { -// value: UntaggedValue::Row(_), -// .. -// } => { -// let mut shell_error = ShellError::type_error("binary data", v.spanned_type_name()); -// shell_error.notes.push( -// "Note: you can access columns using dot. eg) $it.column or (ls).column".into(), -// ); -// Err(shell_error) -// } -// v => Err(ShellError::type_error("binary data", v.spanned_type_name())), -// } -// } -// } +impl FromValue for Vec { + fn from_value(v: &Value) -> Result { + match v { + Value::Binary { val, .. } => Ok(val.clone()), + Value::String { val, .. } => Ok(val.bytes().collect()), + v => Err(ShellError::CantConvert("binary data".into(), v.span()?)), + } + } +} // impl FromValue for Dictionary { // fn from_value(v: &Value) -> Result { diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 88a1880ddf..befcd029f3 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -19,6 +19,10 @@ pub enum ShellError { rhs_span: Span, }, + #[error("Operator overflow.")] + #[diagnostic(code(nu::shell::operator_overflow), url(docsrs))] + OperatorOverflow(String, #[label = "{0}"] Span), + #[error("Pipeline mismatch.")] #[diagnostic(code(nu::shell::pipeline_mismatch), url(docsrs))] PipelineMismatch { diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index e792e253fc..976cb9b6b3 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -363,13 +363,6 @@ impl Value { Ok(current) } - pub fn string(s: &str, span: Span) -> Value { - Value::String { - val: s.into(), - span, - } - } - pub fn is_true(&self) -> bool { matches!(self, Value::Bool { val: true, .. }) } @@ -438,6 +431,33 @@ impl Value { }, } } + + pub fn string(val: impl Into, span: Span) -> Value { + Value::String { + val: val.into(), + span, + } + } + + pub fn int(val: i64, span: Span) -> Value { + Value::Int { val, span } + } + + // Only use these for test data. Span::unknown() should not be used in user data + pub fn test_string(s: impl Into) -> Value { + Value::String { + val: s.into(), + span: Span::unknown(), + } + } + + // Only use these for test data. Span::unknown() should not be used in user data + pub fn test_int(val: i64) -> Value { + Value::Int { + val, + span: Span::unknown(), + } + } } impl PartialOrd for Value { @@ -528,10 +548,16 @@ impl Value { let span = span(&[self.span()?, rhs.span()?]); match (self, rhs) { - (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int { - val: lhs + rhs, - span, - }), + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if let Some(val) = lhs.checked_add(*rhs) { + Ok(Value::Int { val, span }) + } else { + Err(ShellError::OperatorOverflow( + "add operation overflowed".into(), + span, + )) + } + } (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float { val: *lhs as f64 + *rhs, span,