From 0961da406dcd4a65571fe0ea94fb0dff06d7dcd3 Mon Sep 17 00:00:00 2001 From: Corvus Corax Date: Thu, 13 Feb 2020 09:47:04 -0600 Subject: [PATCH] Add string to datetime to str plugin (#1381) * Add string to datetime to str plugin * Test string to date/time conversion --- Cargo.lock | 1 + crates/nu-protocol/src/value.rs | 5 ++++ crates/nu_plugin_str/Cargo.toml | 1 + crates/nu_plugin_str/src/nu/mod.rs | 11 ++++++++ crates/nu_plugin_str/src/nu/tests.rs | 32 +++++++++++++++++++++- crates/nu_plugin_str/src/strutils.rs | 40 +++++++++++++++------------- 6 files changed, 70 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5929e5663c..5cc41edb99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2555,6 +2555,7 @@ dependencies = [ name = "nu_plugin_str" version = "0.9.0" dependencies = [ + "chrono", "nu-build", "nu-errors", "nu-plugin", diff --git a/crates/nu-protocol/src/value.rs b/crates/nu-protocol/src/value.rs index 9a1600aad2..beaf2a557f 100644 --- a/crates/nu-protocol/src/value.rs +++ b/crates/nu-protocol/src/value.rs @@ -15,6 +15,7 @@ use crate::value::primitive::Primitive; use crate::value::range::{Range, RangeInclusion}; use crate::{ColumnPath, PathMember}; use bigdecimal::BigDecimal; +use chrono::{DateTime, Utc}; use indexmap::IndexMap; use nu_errors::ShellError; use nu_source::{AnchorLocation, HasSpan, Span, Spanned, Tag}; @@ -201,6 +202,10 @@ impl UntaggedValue { UntaggedValue::Primitive(Primitive::Date(s.into())) } + pub fn date(d: impl Into>) -> UntaggedValue { + UntaggedValue::Primitive(Primitive::Date(d.into())) + } + /// Helper for creating the Nothing value pub fn nothing() -> UntaggedValue { UntaggedValue::Primitive(Primitive::Nothing) diff --git a/crates/nu_plugin_str/Cargo.toml b/crates/nu_plugin_str/Cargo.toml index 9b5764709b..5406711507 100644 --- a/crates/nu_plugin_str/Cargo.toml +++ b/crates/nu_plugin_str/Cargo.toml @@ -15,6 +15,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.9.0" } nu-source = { path = "../nu-source", version = "0.9.0" } nu-errors = { path = "../nu-errors", version = "0.9.0" } nu-value-ext = { path = "../nu-value-ext", version = "0.9.0" } +chrono = { version = "0.4.10", features = ["serde"] } regex = "1" num-bigint = "0.2.3" diff --git a/crates/nu_plugin_str/src/nu/mod.rs b/crates/nu_plugin_str/src/nu/mod.rs index c74c821458..b8d12f4c2f 100644 --- a/crates/nu_plugin_str/src/nu/mod.rs +++ b/crates/nu_plugin_str/src/nu/mod.rs @@ -36,6 +36,12 @@ impl Plugin for Str { "convert string to portion of original, requires \"start,end\"", Some('s'), ) + .named( + "to-date-time", + SyntaxShape::String, + "Convert string to Date/Time", + Some('D'), + ) .rest(SyntaxShape::ColumnPath, "the column(s) to convert") .filter()) } @@ -117,6 +123,11 @@ impl Plugin for Str { self.for_field(possible_field); } + if let Some(dt) = args.get("to-date-time") { + let dt = dt.as_string()?; + self.for_date_time(dt); + } + match &self.error { Some(reason) => Err(ShellError::untagged_runtime_error(format!( "{}: {}", diff --git a/crates/nu_plugin_str/src/nu/tests.rs b/crates/nu_plugin_str/src/nu/tests.rs index 5d33dbb9b4..205a2b30fe 100644 --- a/crates/nu_plugin_str/src/nu/tests.rs +++ b/crates/nu_plugin_str/src/nu/tests.rs @@ -7,7 +7,37 @@ mod integration { unstructured_sample_record, }; use nu_plugin::test_helpers::{expect_return_value_at, plugin, CallStub}; - use nu_protocol::UntaggedValue; + use nu_protocol::{Primitive, UntaggedValue}; + + #[test] + fn picks_up_date_time() { + let run = plugin(&mut Str::new()) + .args( + CallStub::new() + .with_named_parameter("to-date-time", string("%d.%m.%Y %H:%M %P %z")) + .create(), + ) + .input(string("5.8.1994 8:00 am +0000")) + .input(string("6.9.1995 10:00 am +0000")) + .input(string("5.8.1994 20:00 pm +0000")) + .input(string("20.4.2020 8:00 am +0000")) + .setup(|_, _| {}) + .test(); + let ret_vals = run.unwrap(); + for r in ret_vals { + let r = r + .as_ref() + .unwrap() + .raw_value() + .unwrap() + .as_primitive() + .unwrap(); + match r { + Primitive::Date(_) => (), + _ => assert!(false, "failed to convert string to date"), + } + } + } #[test] fn picks_up_one_action_flag_only() { diff --git a/crates/nu_plugin_str/src/strutils.rs b/crates/nu_plugin_str/src/strutils.rs index e275465c80..cf095ef1e2 100644 --- a/crates/nu_plugin_str/src/strutils.rs +++ b/crates/nu_plugin_str/src/strutils.rs @@ -1,3 +1,6 @@ +extern crate chrono; + +use chrono::DateTime; use nu_errors::ShellError; use nu_protocol::{did_you_mean, ColumnPath, Primitive, ShellTypeName, UntaggedValue, Value}; use nu_source::{span_for_spanned_list, Tagged}; @@ -12,6 +15,7 @@ pub enum Action { ToInteger, Substring(usize, usize), Replace(ReplaceAction), + ToDateTime(String), } #[derive(Debug, Eq, PartialEq)] @@ -70,6 +74,10 @@ impl Str { Err(_) => UntaggedValue::string(input), }, }, + Some(Action::ToDateTime(dt)) => match DateTime::parse_from_str(input, dt) { + Ok(d) => UntaggedValue::date(d), + Err(_) => UntaggedValue::string(input), + }, None => UntaggedValue::string(input), }; @@ -89,27 +97,15 @@ impl Str { } pub fn for_to_int(&mut self) { - if self.permit() { - self.action = Some(Action::ToInteger); - } else { - self.log_error("can only apply one"); - } + self.add_action(Action::ToInteger); } pub fn for_downcase(&mut self) { - if self.permit() { - self.action = Some(Action::Downcase); - } else { - self.log_error("can only apply one"); - } + self.add_action(Action::Downcase); } pub fn for_upcase(&mut self) { - if self.permit() { - self.action = Some(Action::Upcase); - } else { - self.log_error("can only apply one"); - } + self.add_action(Action::Upcase); } pub fn for_substring(&mut self, s: String) -> Result<(), ShellError> { @@ -130,18 +126,24 @@ impl Str { }; if start > end { self.log_error("End must be greater than or equal to Start"); - } else if self.permit() { - self.action = Some(Action::Substring(start, end)); } else { - self.log_error("can only apply one"); + self.add_action(Action::Substring(start, end)); } Ok(()) } pub fn for_replace(&mut self, mode: ReplaceAction) { + self.add_action(Action::Replace(mode)); + } + + pub fn for_date_time(&mut self, dt: String) { + self.add_action(Action::ToDateTime(dt)); + } + + fn add_action(&mut self, act: Action) { if self.permit() { - self.action = Some(Action::Replace(mode)); + self.action = Some(act); } else { self.log_error("can only apply one"); }