forked from extern/nushell
nu-cli refactor moving commands into their own crate nu-command (#2910)
* move commands, futures.rs, script.rs, utils * move over maybe_print_errors * add nu_command crate references to nu_cli * in commands.rs open up to pub mod from pub(crate) * nu-cli, nu-command, and nu tests are now passing * cargo fmt * clean up nu-cli/src/prelude.rs * code cleanup * for some reason lex.rs was not formatted, may be causing my error * remove mod completion from lib.rs which was not being used along with quickcheck macros * add in allow unused imports * comment out one failing external test; comment out one failing internal test * revert commenting out failing tests; something else might be going on; someone with a windows machine should check and see what is going on with these failing windows tests * Update Cargo.toml Extend the optional features to nu-command Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
This commit is contained in:
40
crates/nu-command/src/commands/date/command.rs
Normal file
40
crates/nu-command/src/commands/date/command.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Apply date function"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_help(&Command, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Command {})?)
|
||||
}
|
||||
}
|
109
crates/nu-command/src/commands/date/format.rs
Normal file
109
crates/nu-command/src/commands/date/format.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
Dictionary, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use std::fmt::{self, write};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FormatArgs {
|
||||
format: Tagged<String>,
|
||||
table: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date format"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date format")
|
||||
.required("format", SyntaxShape::String, "strftime format")
|
||||
.switch("table", "print date in a table", Some('t'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Format a given date using the given format string."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
format(args).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Format the current date",
|
||||
example: "date now | date format '%Y.%m.%d_%H %M %S,%z'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Format the current date and print in a table",
|
||||
example: "date now | date format -t '%Y-%m-%d_%H:%M:%S %z'",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (FormatArgs { format, table }, input) = args.process().await?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Date(dt)),
|
||||
..
|
||||
} => {
|
||||
let mut output = String::new();
|
||||
if let Err(fmt::Error) =
|
||||
write(&mut output, format_args!("{}", dt.format(&format.item)))
|
||||
{
|
||||
Err(ShellError::labeled_error(
|
||||
"The date format is invalid",
|
||||
"invalid strftime format",
|
||||
&format.tag,
|
||||
))
|
||||
} else {
|
||||
let value = if table {
|
||||
let mut indexmap = IndexMap::new();
|
||||
indexmap.insert(
|
||||
"formatted".to_string(),
|
||||
UntaggedValue::string(&output).into_value(&tag),
|
||||
);
|
||||
|
||||
UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag)
|
||||
} else {
|
||||
UntaggedValue::string(&output).into_value(&tag)
|
||||
};
|
||||
|
||||
ReturnSuccess::value(value)
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Expected a date from pipeline",
|
||||
"requires date input",
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
75
crates/nu-command/src/commands/date/list_timezone.rs
Normal file
75
crates/nu-command/src/commands/date/list_timezone.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use crate::prelude::*;
|
||||
use chrono_tz::TZ_VARIANTS;
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date list-timezone"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date list-timezone")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"List supported time zones."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
list_timezone(args).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "List all supported time zones",
|
||||
example: "date list-timezone",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "List all supported European time zones",
|
||||
example: "date list-timezone | where timezone =~ Europe",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let list = TZ_VARIANTS.iter().map(move |tz| {
|
||||
let mut entries = IndexMap::new();
|
||||
|
||||
entries.insert(
|
||||
"timezone".to_string(),
|
||||
UntaggedValue::string(tz.name()).into_value(&tag),
|
||||
);
|
||||
|
||||
Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::Row(Dictionary { entries }).into_value(&tag),
|
||||
))
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(list).to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
15
crates/nu-command/src/commands/date/mod.rs
Normal file
15
crates/nu-command/src/commands/date/mod.rs
Normal file
@ -0,0 +1,15 @@
|
||||
pub mod command;
|
||||
pub mod format;
|
||||
pub mod list_timezone;
|
||||
pub mod now;
|
||||
pub mod to_table;
|
||||
pub mod to_timezone;
|
||||
|
||||
mod parser;
|
||||
|
||||
pub use command::Command as Date;
|
||||
pub use format::Date as DateFormat;
|
||||
pub use list_timezone::Date as DateListTimeZone;
|
||||
pub use now::Date as DateNow;
|
||||
pub use to_table::Date as DateToTable;
|
||||
pub use to_timezone::Date as DateToTimeZone;
|
50
crates/nu-command/src/commands/date/now.rs
Normal file
50
crates/nu-command/src/commands/date/now.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use crate::prelude::*;
|
||||
use chrono::{DateTime, Local};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date now"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date now")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Get the current date."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
now(args).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn now(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let now: DateTime<Local> = Local::now();
|
||||
|
||||
let value = UntaggedValue::date(now.with_timezone(now.offset())).into_value(&tag);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
107
crates/nu-command/src/commands/date/parser.rs
Normal file
107
crates/nu-command/src/commands/date/parser.rs
Normal file
@ -0,0 +1,107 @@
|
||||
// Modified from chrono::format::scan
|
||||
|
||||
use chrono::{DateTime, FixedOffset, Local, Offset, TimeZone};
|
||||
use chrono_tz::Tz;
|
||||
use titlecase::titlecase;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum ParseErrorKind {
|
||||
/// Given field is out of permitted range.
|
||||
OutOfRange,
|
||||
|
||||
/// The input string has some invalid character sequence for given formatting items.
|
||||
Invalid,
|
||||
|
||||
/// The input string has been prematurely ended.
|
||||
TooShort,
|
||||
}
|
||||
|
||||
pub fn datetime_in_timezone(
|
||||
dt: &DateTime<FixedOffset>,
|
||||
s: &str,
|
||||
) -> Result<DateTime<FixedOffset>, ParseErrorKind> {
|
||||
match timezone_offset_internal(s, true, true) {
|
||||
Ok(offset) => match FixedOffset::east_opt(offset) {
|
||||
Some(offset) => Ok(dt.with_timezone(&offset)),
|
||||
None => Err(ParseErrorKind::OutOfRange),
|
||||
},
|
||||
Err(ParseErrorKind::Invalid) => {
|
||||
if s.to_lowercase() == "local" {
|
||||
Ok(dt.with_timezone(Local::now().offset()))
|
||||
} else {
|
||||
let tz: Tz = parse_timezone_internal(s)?;
|
||||
let offset = tz.offset_from_utc_datetime(&dt.naive_utc()).fix();
|
||||
Ok(dt.with_timezone(&offset))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_timezone_internal(s: &str) -> Result<Tz, ParseErrorKind> {
|
||||
if let Ok(tz) = s.parse() {
|
||||
Ok(tz)
|
||||
} else if let Ok(tz) = titlecase(s).parse() {
|
||||
Ok(tz)
|
||||
} else if let Ok(tz) = s.to_uppercase().parse() {
|
||||
Ok(tz)
|
||||
} else {
|
||||
Err(ParseErrorKind::Invalid)
|
||||
}
|
||||
}
|
||||
|
||||
fn timezone_offset_internal(
|
||||
mut s: &str,
|
||||
consume_colon: bool,
|
||||
allow_missing_minutes: bool,
|
||||
) -> Result<i32, ParseErrorKind> {
|
||||
fn digits(s: &str) -> Result<(u8, u8), ParseErrorKind> {
|
||||
let b = s.as_bytes();
|
||||
if b.len() < 2 {
|
||||
Err(ParseErrorKind::TooShort)
|
||||
} else {
|
||||
Ok((b[0], b[1]))
|
||||
}
|
||||
}
|
||||
let negative = match s.as_bytes().first() {
|
||||
Some(&b'+') => false,
|
||||
Some(&b'-') => true,
|
||||
Some(_) => return Err(ParseErrorKind::Invalid),
|
||||
None => return Err(ParseErrorKind::TooShort),
|
||||
};
|
||||
s = &s[1..];
|
||||
|
||||
// hours (00--99)
|
||||
let hours = match digits(s)? {
|
||||
(h1 @ b'0'..=b'9', h2 @ b'0'..=b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
|
||||
_ => return Err(ParseErrorKind::Invalid),
|
||||
};
|
||||
s = &s[2..];
|
||||
|
||||
// colons (and possibly other separators)
|
||||
if consume_colon {
|
||||
s = s.trim_start_matches(|c: char| c == ':' || c.is_whitespace());
|
||||
}
|
||||
|
||||
// minutes (00--59)
|
||||
// if the next two items are digits then we have to add minutes
|
||||
let minutes = if let Ok(ds) = digits(s) {
|
||||
match ds {
|
||||
(m1 @ b'0'..=b'5', m2 @ b'0'..=b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
|
||||
(b'6'..=b'9', b'0'..=b'9') => return Err(ParseErrorKind::OutOfRange),
|
||||
_ => return Err(ParseErrorKind::Invalid),
|
||||
}
|
||||
} else if allow_missing_minutes {
|
||||
0
|
||||
} else {
|
||||
return Err(ParseErrorKind::TooShort);
|
||||
};
|
||||
match s.len() {
|
||||
len if len >= 2 => &s[2..],
|
||||
len if len == 0 => s,
|
||||
_ => return Err(ParseErrorKind::TooShort),
|
||||
};
|
||||
|
||||
let seconds = hours * 3600 + minutes * 60;
|
||||
Ok(if negative { -seconds } else { seconds })
|
||||
}
|
105
crates/nu-command/src/commands/date/to_table.rs
Normal file
105
crates/nu-command/src/commands/date/to_table.rs
Normal file
@ -0,0 +1,105 @@
|
||||
use crate::prelude::*;
|
||||
use chrono::{Datelike, Timelike};
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date to-table"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date to-table")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Print the date in a structured table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_table(args).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Print the current date in a table",
|
||||
example: "date now | date to-table",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
async fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let input = args.input;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Date(dt)),
|
||||
..
|
||||
} => {
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
||||
indexmap.insert(
|
||||
"year".to_string(),
|
||||
UntaggedValue::int(dt.year()).into_value(&tag),
|
||||
);
|
||||
indexmap.insert(
|
||||
"month".to_string(),
|
||||
UntaggedValue::int(dt.month()).into_value(&tag),
|
||||
);
|
||||
indexmap.insert(
|
||||
"day".to_string(),
|
||||
UntaggedValue::int(dt.day()).into_value(&tag),
|
||||
);
|
||||
indexmap.insert(
|
||||
"hour".to_string(),
|
||||
UntaggedValue::int(dt.hour()).into_value(&tag),
|
||||
);
|
||||
indexmap.insert(
|
||||
"minute".to_string(),
|
||||
UntaggedValue::int(dt.minute()).into_value(&tag),
|
||||
);
|
||||
indexmap.insert(
|
||||
"second".to_string(),
|
||||
UntaggedValue::int(dt.second()).into_value(&tag),
|
||||
);
|
||||
|
||||
let tz = dt.offset();
|
||||
indexmap.insert(
|
||||
"timezone".to_string(),
|
||||
UntaggedValue::string(format!("{}", tz)).into_value(&tag),
|
||||
);
|
||||
|
||||
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
|
||||
|
||||
ReturnSuccess::value(value)
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Expected a date from pipeline",
|
||||
"requires date input",
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
110
crates/nu-command/src/commands/date/to_timezone.rs
Normal file
110
crates/nu-command/src/commands/date/to_timezone.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::commands::date::parser::{datetime_in_timezone, ParseErrorKind};
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DateToTimeZoneArgs {
|
||||
timezone: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date to-timezone"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date to-timezone").required(
|
||||
"time zone",
|
||||
SyntaxShape::String,
|
||||
"time zone description",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert a date to a given time zone.
|
||||
|
||||
Use `date list-timezone` to list all supported time zones.
|
||||
"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_timezone(args).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Get the current date in UTC+05:00",
|
||||
example: "date now | date to-timezone +0500",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Get the current local date",
|
||||
example: "date now | date to-timezone local",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Get the current date in Hawaii",
|
||||
example: "date now | date to-timezone US/Hawaii",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
async fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (DateToTimeZoneArgs { timezone }, input) = args.process().await?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Date(dt)),
|
||||
..
|
||||
} => match datetime_in_timezone(&dt, &timezone.item) {
|
||||
Ok(dt) => {
|
||||
let value = UntaggedValue::date(dt).into_value(&tag);
|
||||
|
||||
ReturnSuccess::value(value)
|
||||
}
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
error_message(e),
|
||||
"invalid time zone",
|
||||
&timezone.tag,
|
||||
)),
|
||||
},
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Expected a date from pipeline",
|
||||
"requires date input",
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn error_message(err: ParseErrorKind) -> &'static str {
|
||||
match err {
|
||||
ParseErrorKind::Invalid => "The time zone description is invalid",
|
||||
ParseErrorKind::OutOfRange => "The time zone offset is out of range",
|
||||
ParseErrorKind::TooShort => "The format of the time zone is invalid",
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
55
crates/nu-command/src/commands/date/utc.rs
Normal file
55
crates/nu-command/src/commands/date/utc.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::prelude::*;
|
||||
use chrono::{DateTime, Utc};
|
||||
use nu_errors::ShellError;
|
||||
|
||||
use crate::commands::date::utils::date_to_value;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::Signature;
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date utc"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date utc")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"return the current date in utc."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
utc(args).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn utc(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let no_fmt = "".to_string();
|
||||
|
||||
let value = {
|
||||
let local: DateTime<Utc> = Utc::now();
|
||||
date_to_value(local, tag, no_fmt)
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user