diff --git a/Cargo.lock b/Cargo.lock index 4d3e8eb37e..a2a98c189c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -611,15 +611,15 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c" +checksum = "77b75a27dc8d220f1f8521ea69cd55a34d720a200ebb3a624d9aa19193d3b432" dependencies = [ "bitflags", "crossterm_winapi", "libc", "mio", - "parking_lot", + "parking_lot 0.12.0", "serde", "signal-hook", "signal-hook-mio", @@ -996,7 +996,7 @@ checksum = "fcef756dea9cf3db5ce73759cf0467330427a786b47711b8d6c97620d718ceb9" dependencies = [ "cfg-if", "rustix", - "windows-sys", + "windows-sys 0.30.0", ] [[package]] @@ -1827,9 +1827,9 @@ dependencies = [ [[package]] name = "lscolors" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd58d8727f3035fa6d5272f16b519741fd4875936b99d8a7cde21291b7d9174" +checksum = "4e9323b3525d4efad2dead1837a105e313253bfdbad1d470994038eededa4d62" dependencies = [ "ansi_term", "crossterm", @@ -2278,6 +2278,7 @@ dependencies = [ name = "nu-parser" version = "0.59.0" dependencies = [ + "chrono", "log", "miette", "nu-path", @@ -2673,7 +2674,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.1", ] [[package]] @@ -2690,6 +2701,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.10", + "smallvec", + "windows-sys 0.32.0", +] + [[package]] name = "parquet-format-async-temp" version = "0.2.0" @@ -3314,7 +3338,7 @@ dependencies = [ [[package]] name = "reedline" version = "0.2.0" -source = "git+https://github.com/nushell/reedline?branch=main#2db0ffd1c48ec1c03c538822900a368b40eec0a8" +source = "git+https://github.com/nushell/reedline?branch=main#65cfd6bcbd117049a048fdaf6d125880195cd184" dependencies = [ "chrono", "crossterm", @@ -3714,7 +3738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" dependencies = [ "lazy_static", - "parking_lot", + "parking_lot 0.11.2", "serial_test_derive", ] @@ -3910,7 +3934,7 @@ checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26" dependencies = [ "lazy_static", "new_debug_unreachable", - "parking_lot", + "parking_lot 0.11.2", "phf_shared 0.10.0", "precomputed-hash", "serde", @@ -4650,11 +4674,24 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.30.0", + "windows_i686_gnu 0.30.0", + "windows_i686_msvc 0.30.0", + "windows_x86_64_gnu 0.30.0", + "windows_x86_64_msvc 0.30.0", +] + +[[package]] +name = "windows-sys" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +dependencies = [ + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", ] [[package]] @@ -4663,6 +4700,12 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + [[package]] name = "windows_gen" version = "0.9.1" @@ -4678,12 +4721,24 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + [[package]] name = "windows_i686_msvc" version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + [[package]] name = "windows_macros" version = "0.9.1" @@ -4700,12 +4755,24 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + [[package]] name = "windows_x86_64_msvc" version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + [[package]] name = "winreg" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 7085066ccc..8524322900 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ members = [ [dependencies] chrono = "0.4.19" -crossterm = "0.22.*" +crossterm = "0.23" crossterm_winapi = "0.9.0" ctrlc = "3.2.1" # lazy_static = "1.4.0" diff --git a/crates/nu-cli/src/syntax_highlight.rs b/crates/nu-cli/src/syntax_highlight.rs index 187a80ea16..789b208fe8 100644 --- a/crates/nu-cli/src/syntax_highlight.rs +++ b/crates/nu-cli/src/syntax_highlight.rs @@ -131,6 +131,13 @@ impl Highlighter for NuHighlighter { next_token, )) } + FlatShape::DateTime => { + // nushell ??? + output.push(( + get_shape_color(shape.1.to_string(), &self.config), + next_token, + )) + } FlatShape::List => { // nushell ??? output.push(( diff --git a/crates/nu-color-config/src/shape_color.rs b/crates/nu-color-config/src/shape_color.rs index ccc13044e2..8324a0c7e8 100644 --- a/crates/nu-color-config/src/shape_color.rs +++ b/crates/nu-color-config/src/shape_color.rs @@ -22,6 +22,7 @@ pub fn get_shape_color(shape: String, conf: &Config) -> Style { "shape_signature" => Style::new().fg(Color::Green).bold(), "shape_string" => Style::new().fg(Color::Green), "shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(), + "shape_datetime" => Style::new().fg(Color::Cyan).bold(), "shape_list" => Style::new().fg(Color::Cyan).bold(), "shape_table" => Style::new().fg(Color::Blue).bold(), "shape_record" => Style::new().fg(Color::Cyan).bold(), diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index e67d249013..c1f05dc8af 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -30,7 +30,7 @@ calamine = "0.18.0" chrono = { version = "0.4.19", features = ["serde"] } chrono-humanize = "0.2.1" chrono-tz = "0.6.0" -crossterm = "0.22.1" +crossterm = "0.23" csv = "1.1.3" dialoguer = "0.9.0" digest = "0.10.0" @@ -47,7 +47,7 @@ Inflector = "0.11" itertools = "0.10.0" lazy_static = "1.4.0" log = "0.4.14" -lscolors = { version = "0.8.0", features = ["crossterm"] } +lscolors = { version = "0.9.0", features = ["crossterm"] } md5 = { package = "md-5", version = "0.10.0" } meval = "0.2.0" mime = "0.3.16" diff --git a/crates/nu-command/src/filters/update_cells.rs b/crates/nu-command/src/filters/update_cells.rs index f578b132cd..7d29842ad2 100644 --- a/crates/nu-command/src/filters/update_cells.rs +++ b/crates/nu-command/src/filters/update_cells.rs @@ -41,8 +41,8 @@ impl Command for UpdateCells { Example { description: "Update the zero value cells to empty strings.", example: r#"[ - [2021-04-16, 2021-06-10, 2021-09-18, 2021-10-15, 2021-11-16, 2021-11-17, 2021-11-18]; - [ 37, 0, 0, 0, 37, 0, 0] + ["2021-04-16", "2021-06-10", "2021-09-18", "2021-10-15", "2021-11-16", "2021-11-17", "2021-11-18"]; + [ 37, 0, 0, 0, 37, 0, 0] ] | update cells {|value| if $value == 0 { "" @@ -78,8 +78,8 @@ impl Command for UpdateCells { Example { description: "Update the zero value cells to empty strings in 2 last columns.", example: r#"[ - [2021-04-16, 2021-06-10, 2021-09-18, 2021-10-15, 2021-11-16, 2021-11-17, 2021-11-18]; - [ 37, 0, 0, 0, 37, 0, 0] + ["2021-04-16", "2021-06-10", "2021-09-18", "2021-10-15", "2021-11-16", "2021-11-17", "2021-11-18"]; + [ 37, 0, 0, 0, 37, 0, 0] ] | update cells -c ["2021-11-18", "2021-11-17"] {|value| if $value == 0 { "" diff --git a/crates/nu-command/src/formats/from/nuon.rs b/crates/nu-command/src/formats/from/nuon.rs index b55d1f6431..2f9b1d273d 100644 --- a/crates/nu-command/src/formats/from/nuon.rs +++ b/crates/nu-command/src/formats/from/nuon.rs @@ -212,6 +212,7 @@ fn convert_to_value( "subexpressions and cellpaths not supported in nuon".into(), expr.span, )), + Expr::DateTime(dt) => Ok(Value::Date { val: dt, span }), Expr::ExternalCall(..) => Err(ShellError::OutsideSpannedLabeledError( original_text.to_string(), "Error when loading".into(), diff --git a/crates/nu-command/src/formats/to/nuon.rs b/crates/nu-command/src/formats/to/nuon.rs index 16c3268423..d3bac4bd3d 100644 --- a/crates/nu-command/src/formats/to/nuon.rs +++ b/crates/nu-command/src/formats/to/nuon.rs @@ -57,7 +57,7 @@ fn value_to_string(v: &Value, span: Span) -> Result { } Value::CellPath { .. } => Err(ShellError::UnsupportedInput("cellpath".to_string(), span)), Value::CustomValue { .. } => Err(ShellError::UnsupportedInput("custom".to_string(), span)), - Value::Date { .. } => Err(ShellError::UnsupportedInput("date".to_string(), span)), + Value::Date { val, .. } => Ok(val.to_rfc3339()), Value::Duration { val, .. } => Ok(format!("{}ns", *val)), Value::Error { .. } => Err(ShellError::UnsupportedInput("error".to_string(), span)), Value::Filesize { val, .. } => Ok(format!("{}b", *val)), diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index df7d044372..93fc2c4853 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -292,6 +292,10 @@ pub fn eval_expression( )? .into_value(span)) } + Expr::DateTime(dt) => Ok(Value::Date { + val: *dt, + span: expr.span, + }), Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }), Expr::BinaryOp(lhs, op, rhs) => { let op_span = op.span; diff --git a/crates/nu-parser/Cargo.toml b/crates/nu-parser/Cargo.toml index c0a551667c..183b69030d 100644 --- a/crates/nu-parser/Cargo.toml +++ b/crates/nu-parser/Cargo.toml @@ -4,6 +4,7 @@ version = "0.59.0" edition = "2021" [dependencies] +chrono = "0.4.19" miette = "4.1.0" thiserror = "1.0.29" serde_json = "1.0" diff --git a/crates/nu-parser/src/flatten.rs b/crates/nu-parser/src/flatten.rs index f7839897c0..99579ad7a5 100644 --- a/crates/nu-parser/src/flatten.rs +++ b/crates/nu-parser/src/flatten.rs @@ -23,6 +23,7 @@ pub enum FlatShape { Record, Block, Filepath, + DateTime, GlobPattern, Variable, Flag, @@ -45,6 +46,7 @@ impl Display for FlatShape { FlatShape::Operator => write!(f, "shape_operator"), FlatShape::Signature => write!(f, "shape_signature"), FlatShape::String => write!(f, "shape_string"), + FlatShape::DateTime => write!(f, "shape_datetime"), FlatShape::StringInterpolation => write!(f, "shape_string_interpolation"), FlatShape::List => write!(f, "shape_list"), FlatShape::Table => write!(f, "shape_table"), @@ -184,6 +186,9 @@ pub fn flatten_expression( Expr::Nothing => { vec![(expr.span, FlatShape::Nothing)] } + Expr::DateTime(_) => { + vec![(expr.span, FlatShape::DateTime)] + } Expr::Int(_) => { vec![(expr.span, FlatShape::Int)] } diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index f7a2f8ae00..93d8d19677 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -1704,6 +1704,65 @@ pub fn parse_filepath( } } +/// Parse a datetime type, eg '2022-02-02' +pub fn parse_datetime( + working_set: &mut StateWorkingSet, + span: Span, +) -> (Expression, Option) { + trace!("parsing: datetime"); + + let bytes = working_set.get_span_contents(span); + let token = String::from_utf8_lossy(bytes).to_string(); + + if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&token) { + return ( + Expression { + expr: Expr::DateTime(datetime), + span, + ty: Type::Date, + custom_completion: None, + }, + None, + ); + } + + // Just the date + if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&format!("{}T00:00:00+00:00", token)) + { + return ( + Expression { + expr: Expr::DateTime(datetime), + span, + ty: Type::Date, + custom_completion: None, + }, + None, + ); + } + + // Date and time, assume UTC + if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&format!("{}+00:00", token)) { + return ( + Expression { + expr: Expr::DateTime(datetime), + span, + ty: Type::Date, + custom_completion: None, + }, + None, + ); + } + + ( + garbage(span), + Some(ParseError::Mismatch( + "datetime".into(), + "non-datetime".into(), + span, + )), + ) +} + /// Parse a duration type, eg '10day' pub fn parse_duration( working_set: &mut StateWorkingSet, @@ -3115,6 +3174,7 @@ pub fn parse_value( SyntaxShape::Number => parse_number(bytes, span), SyntaxShape::Int => parse_int(bytes, span), SyntaxShape::Duration => parse_duration(working_set, span), + SyntaxShape::DateTime => parse_datetime(working_set, span), SyntaxShape::Filesize => parse_filesize(working_set, span), SyntaxShape::Range => parse_range(working_set, span), SyntaxShape::Filepath => parse_filepath(working_set, span), @@ -3212,6 +3272,7 @@ pub fn parse_value( SyntaxShape::Int, SyntaxShape::Number, SyntaxShape::Range, + SyntaxShape::DateTime, SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Block(None), @@ -3950,6 +4011,7 @@ pub fn discover_captures_in_expr( } } Expr::CellPath(_) => {} + Expr::DateTime(_) => {} Expr::ExternalCall(head, exprs) => { let result = discover_captures_in_expr(working_set, head, seen, seen_blocks); output.extend(&result); diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index 217b2643ec..822a4ec201 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -31,6 +31,7 @@ pub fn math_result_type( (Type::Int, Type::Float) => (Type::Float, None), (Type::Float, Type::Float) => (Type::Float, None), (Type::String, Type::String) => (Type::String, None), + (Type::Date, Type::Duration) => (Type::Date, None), (Type::Duration, Type::Duration) => (Type::Duration, None), (Type::Filesize, Type::Filesize) => (Type::Filesize, None), @@ -69,6 +70,7 @@ pub fn math_result_type( (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), (Type::Float, Type::Float) => (Type::Float, None), + (Type::Date, Type::Date) => (Type::Duration, None), (Type::Duration, Type::Duration) => (Type::Duration, None), (Type::Filesize, Type::Filesize) => (Type::Filesize, None), diff --git a/crates/nu-protocol/src/ast/expr.rs b/crates/nu-protocol/src/ast/expr.rs index cc6d532251..e27c78b26a 100644 --- a/crates/nu-protocol/src/ast/expr.rs +++ b/crates/nu-protocol/src/ast/expr.rs @@ -1,3 +1,5 @@ +use chrono::FixedOffset; + use super::{Call, CellPath, Expression, FullCellPath, Operator, RangeOperator}; use crate::{ast::ImportPattern, BlockId, Signature, Span, Spanned, Unit, VarId}; @@ -26,6 +28,7 @@ pub enum Expr { Record(Vec<(Expression, Expression)>), Keyword(Vec, Span, Box), ValueWithUnit(Box, Spanned), + DateTime(chrono::DateTime), Filepath(String), GlobPattern(String), String(String), diff --git a/crates/nu-protocol/src/ast/expression.rs b/crates/nu-protocol/src/ast/expression.rs index c678c89773..f58bbc91f6 100644 --- a/crates/nu-protocol/src/ast/expression.rs +++ b/crates/nu-protocol/src/ast/expression.rs @@ -142,6 +142,7 @@ impl Expression { false } Expr::CellPath(_) => false, + Expr::DateTime(_) => false, Expr::ExternalCall(head, args) => { if head.has_in_variable(working_set) { return true; @@ -301,6 +302,7 @@ impl Expression { } } Expr::CellPath(_) => {} + Expr::DateTime(_) => {} Expr::ExternalCall(head, args) => { head.replace_in_variable(working_set, new_var_id); for arg in args { @@ -443,6 +445,7 @@ impl Expression { } } Expr::CellPath(_) => {} + Expr::DateTime(_) => {} Expr::ExternalCall(head, args) => { head.replace_span(working_set, replaced, new_span); for arg in args { diff --git a/crates/nu-protocol/src/syntax_shape.rs b/crates/nu-protocol/src/syntax_shape.rs index 90b0dd201f..b3a49af7a2 100644 --- a/crates/nu-protocol/src/syntax_shape.rs +++ b/crates/nu-protocol/src/syntax_shape.rs @@ -55,6 +55,9 @@ pub enum SyntaxShape { /// A duration value is allowed, eg `19day` Duration, + /// A datetime value, eg `2022-02-02` or `2019-10-12T07:20:50.52+00:00` + DateTime, + /// An operator Operator, @@ -94,6 +97,7 @@ impl SyntaxShape { SyntaxShape::Block(_) => Type::Block, SyntaxShape::CellPath => Type::Unknown, SyntaxShape::Custom(custom, _) => custom.to_type(), + SyntaxShape::DateTime => Type::Date, SyntaxShape::Duration => Type::Duration, SyntaxShape::Expression => Type::Unknown, SyntaxShape::Filepath => Type::String, @@ -145,6 +149,7 @@ impl Display for SyntaxShape { SyntaxShape::Record => write!(f, "record"), SyntaxShape::Filesize => write!(f, "filesize"), SyntaxShape::Duration => write!(f, "duration"), + SyntaxShape::DateTime => write!(f, "datetime"), SyntaxShape::Operator => write!(f, "operator"), SyntaxShape::RowCondition => write!(f, "condition"), SyntaxShape::MathExpression => write!(f, "variable"), diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 808a08fce6..b5757abdfb 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -1306,6 +1306,17 @@ impl Value { val: lhs - rhs, span, }), + (Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => { + let result = lhs.signed_duration_since(*rhs); + + match result.num_nanoseconds() { + Some(v) => Ok(Value::Duration { val: v, span }), + None => Err(ShellError::OperatorOverflow( + "subtraction operation overflowed".into(), + span, + )), + } + } (Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => { match lhs.checked_sub_signed(chrono::Duration::nanoseconds(*rhs)) { Some(val) => Ok(Value::Date { val, span }), diff --git a/src/default_config.nu b/src/default_config.nu index aa9a6144cf..59ef88e626 100644 --- a/src/default_config.nu +++ b/src/default_config.nu @@ -146,6 +146,7 @@ let default_theme = { shape_signature: green_bold shape_string: green shape_string_interpolation: cyan_bold + shape_datetime: cyan_bold shape_list: cyan_bold shape_table: blue_bold shape_record: cyan_bold diff --git a/src/tests/test_engine.rs b/src/tests/test_engine.rs index 645091aa2c..df8d2bbfdc 100644 --- a/src/tests/test_engine.rs +++ b/src/tests/test_engine.rs @@ -257,3 +257,8 @@ fn test_redirection_stderr() -> TestResult { // try a nonsense binary run_test(r#"do -i { asdjw4j5cnaabw44rd }; echo done"#, "done") } + +#[test] +fn datetime_literal() -> TestResult { + run_test(r#"(date now) - 2019-08-23 > 1hr"#, "true") +}