From 4587c8c9d4fde75ca70c44f521065c857743e824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Riegel?= Date: Sun, 30 Mar 2025 12:44:33 +0200 Subject: [PATCH] wip: nano second working and error handling --- Cargo.lock | 12 +- .../src/conversions/into/datetime.rs | 135 +++++++++++++++++- .../tests/commands/into_datetime.rs | 10 ++ 3 files changed, 149 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a78d62f77c..9bec7b8e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "array-init-cursor" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed51fe0f224d1d4ea768be38c51f9f831dee9d05c163c11fba0b8c44387b1fc3" +checksum = "bf7d0a018de4f6aa429b9d33d69edf69072b1c5b1cb8d3e4a5f7ef898fc3eb76" [[package]] name = "arrayref" @@ -4488,9 +4488,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -4529,9 +4529,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", diff --git a/crates/nu-command/src/conversions/into/datetime.rs b/crates/nu-command/src/conversions/into/datetime.rs index e503f32653..e559e809cf 100644 --- a/crates/nu-command/src/conversions/into/datetime.rs +++ b/crates/nu-command/src/conversions/into/datetime.rs @@ -1,9 +1,20 @@ use crate::{generate_strftime_list, parse_date_from_string}; -use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, TimeZone, Utc}; -use nu_cmd_base::input_handler::{operate, CmdArgument}; +use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; use nu_engine::command_prelude::*; const HOUR: i32 = 60 * 60; +const ALLOWED_COLUMNS: [&str; 10] = [ + "year", + "month", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", + "timezone", +]; #[derive(Clone, Debug)] struct Arguments { @@ -255,7 +266,86 @@ impl Command for IntoDatetime { #[derive(Clone, Debug)] struct DatetimeFormat(String); +fn merge_record(record: &Record, head: Span, span: Span) -> Value { + for key in record.columns() { + if !ALLOWED_COLUMNS.contains(&key.as_str()) { + let allowed_cols = ALLOWED_COLUMNS.join(", "); + return Value::error(ShellError::UnsupportedInput { + msg: format!( + "Column '{key}' is not valid for a structured datetime. Allowed columns are: {allowed_cols}" + ), + input: "value originates from here".into(), + msg_span: head, + input_span: span + }, + span, + ); + } + } + + let nanosecond: u32 = match record.get("nanosecond") { + Some(val) => match val { + Value::Int { val, .. } => { + dbg!(val); + if *val < 0 || *val > u32::MAX as i64 { + return Value::error( + ShellError::IncorrectValue { + msg: "incorrect value for nanosecond".to_string(), + val_span: head, + call_span: span, + }, + span, + ); + } + *val as u32 + } + other => { + dbg!(other); + return Value::error( + ShellError::OnlySupportsThisInputType { + exp_input_type: "record".to_string(), + wrong_type: other.get_type().to_string(), + dst_span: head, + src_span: other.span(), + }, + span, + ); + } + }, + None => 0, + }; + + dbg!(&nanosecond); + + let timezone = "+01:00"; + + let date = NaiveDate::from_ymd_opt(2025, 1, 1).unwrap(); + let time = match NaiveTime::from_hms_nano_opt(0, 0, 0, nanosecond) { + Some(d) => d, + None => { + return Value::error( + ShellError::IncorrectValue { + msg: "one of more values are incorrect and do not represent valid date time" + .to_string(), + val_span: head, + call_span: span, + }, + span, + ) + } + }; + let datetime = NaiveDateTime::new(date, time); + + let offset: FixedOffset = timezone + .parse() + .unwrap_or(FixedOffset::east_opt(0).unwrap()); // TODO return error instead + + let datetime_with_timezone = DateTime::from_naive_utc_and_offset(datetime, offset); + Value::date(datetime_with_timezone, span) +} + fn action(input: &Value, args: &Arguments, head: Span) -> Value { + dbg!(input, args); let timezone = &args.zone_options; let dateformat = &args.format_options; @@ -264,6 +354,45 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value { return input.clone(); } + if let Value::Record { + val: record, + internal_span, + } = input + { + match timezone { + None => (), + Some(tz) => { + return Value::error( + ShellError::IncompatibleParameters { + left_message: "left message".into(), + left_span: head, + right_message: "right message".into(), + right_span: tz.span, + }, + head, + ); + } + } + // TODO + // match dateformat { + // None => (), + // Some(dt) => { + // return Value::error( + // ShellError::IncompatibleParameters { + // left_message: "left message".into(), + // left_span: head, + // right_message: "right message".into(), + // right_span: dt, + // }, + // head, + // ); + // } + // } + + dbg!(input); + return merge_record(record, head, *internal_span); + } + // Let's try dtparse first if matches!(input, Value::String { .. }) && dateformat.is_none() { let span = input.span(); @@ -281,6 +410,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value { // Propagate errors by explicitly matching them before the final case. Value::Error { .. } => return input.clone(), other => { + dbg!(other); return Value::error( ShellError::OnlySupportsThisInputType { exp_input_type: "string and int".into(), @@ -432,6 +562,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value { } }; + dbg!(input); match input { Value::String { val, .. } => parse_as_string(val), Value::Int { val, .. } => parse_as_string(&val.to_string()), diff --git a/crates/nu-command/tests/commands/into_datetime.rs b/crates/nu-command/tests/commands/into_datetime.rs index b741413e20..71d3137595 100644 --- a/crates/nu-command/tests/commands/into_datetime.rs +++ b/crates/nu-command/tests/commands/into_datetime.rs @@ -1,5 +1,15 @@ use nu_test_support::nu; +#[test] +fn into_datetime_from_record() { + let actual = nu!( + r#"{year: 2023, month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 0, microsecond: 0, nanosecond: 0, timezone: "+01:00"} | into datetime"# + ); + let expected = nu!(r#"'01/01/2023' | into datetime"#); + + assert_eq!(expected.out, actual.out); +} + #[test] fn into_datetime_table_column() { let actual = nu!(r#"[[date]; ["2022-01-01"] ["2023-01-01"]] | into datetime date"#);