mirror of
https://github.com/nushell/nushell.git
synced 2025-05-01 16:44:27 +02:00
move fns in file
This commit is contained in:
parent
e738e6f7d9
commit
a56036aa10
@ -269,229 +269,6 @@ impl Command for IntoDatetime {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct DatetimeFormat(String);
|
struct DatetimeFormat(String);
|
||||||
|
|
||||||
fn parse_value_from_record_as_u32(
|
|
||||||
col: &str,
|
|
||||||
default: u32,
|
|
||||||
record: &Record,
|
|
||||||
head: &Span,
|
|
||||||
span: &Span,
|
|
||||||
) -> Result<u32, Value> {
|
|
||||||
let value: u32 = match record.get(col) {
|
|
||||||
Some(val) => match val {
|
|
||||||
Value::Int { val, .. } => {
|
|
||||||
if *val < 0 || *val > u32::MAX as i64 {
|
|
||||||
return Err(Value::error(
|
|
||||||
ShellError::IncorrectValue {
|
|
||||||
msg: format!("incorrect value for {}", col),
|
|
||||||
val_span: *head,
|
|
||||||
call_span: *span,
|
|
||||||
},
|
|
||||||
*span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
*val as u32
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
dbg!(other);
|
|
||||||
return Err(Value::error(
|
|
||||||
ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "int".to_string(),
|
|
||||||
wrong_type: other.get_type().to_string(),
|
|
||||||
dst_span: *head,
|
|
||||||
src_span: other.span(),
|
|
||||||
},
|
|
||||||
*span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => default,
|
|
||||||
};
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = match parse_value_from_record_as_u32("nanosecond", 0, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let microsecond = match parse_value_from_record_as_u32("microsecond", 0, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let millisecond = match parse_value_from_record_as_u32("millisecond", 0, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let second = match parse_value_from_record_as_u32("second", 0, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let minute = match parse_value_from_record_as_u32("minute", 0, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let hour = match parse_value_from_record_as_u32("hour", 0, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let day = match parse_value_from_record_as_u32("day", 1, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let month = match parse_value_from_record_as_u32("month", 1, record, &head, &span) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let year: i32 = match record.get("year") {
|
|
||||||
Some(val) => match val {
|
|
||||||
Value::Int { val, .. } => *val as i32,
|
|
||||||
other => {
|
|
||||||
dbg!(other);
|
|
||||||
return Value::error(
|
|
||||||
ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "int".to_string(),
|
|
||||||
wrong_type: other.get_type().to_string(),
|
|
||||||
dst_span: head,
|
|
||||||
src_span: other.span(),
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let offset = match record.get("timezone") {
|
|
||||||
Some(val) => match val {
|
|
||||||
Value::String { val, internal_span } => {
|
|
||||||
let offset: FixedOffset = match val.parse() {
|
|
||||||
Ok(offset) => offset,
|
|
||||||
Err(_) => {
|
|
||||||
return Value::error(
|
|
||||||
ShellError::IncorrectValue {
|
|
||||||
msg: "invalid timezone".to_string(),
|
|
||||||
val_span: head,
|
|
||||||
call_span: *internal_span,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
offset
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
dbg!(other);
|
|
||||||
return Value::error(
|
|
||||||
ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "string".to_string(),
|
|
||||||
wrong_type: other.get_type().to_string(),
|
|
||||||
dst_span: head,
|
|
||||||
src_span: other.span(),
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => FixedOffset::east_opt(0).unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
dbg!(&nanosecond);
|
|
||||||
dbg!(µsecond);
|
|
||||||
dbg!(&millisecond);
|
|
||||||
dbg!(&second);
|
|
||||||
dbg!(&minute);
|
|
||||||
dbg!(&hour);
|
|
||||||
dbg!(&day);
|
|
||||||
dbg!(&month);
|
|
||||||
dbg!(&year);
|
|
||||||
dbg!(&offset);
|
|
||||||
|
|
||||||
let total_nanoseconds = nanosecond + microsecond * 1_000 + millisecond * 1_000_000;
|
|
||||||
|
|
||||||
let date = match NaiveDate::from_ymd_opt(year, month, day) {
|
|
||||||
Some(d) => d,
|
|
||||||
None => {
|
|
||||||
return Value::error(
|
|
||||||
ShellError::IncorrectValue {
|
|
||||||
msg: "one of more values are incorrect and do not represent valid date"
|
|
||||||
.to_string(),
|
|
||||||
val_span: head,
|
|
||||||
call_span: span,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let time = match NaiveTime::from_hms_nano_opt(hour, minute, second, total_nanoseconds) {
|
|
||||||
Some(t) => t,
|
|
||||||
None => {
|
|
||||||
return Value::error(
|
|
||||||
ShellError::IncorrectValue {
|
|
||||||
msg: "one of more values are incorrect and do not represent valid time"
|
|
||||||
.to_string(),
|
|
||||||
val_span: head,
|
|
||||||
call_span: span,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let date_time = NaiveDateTime::new(date, time);
|
|
||||||
|
|
||||||
dbg!(&date);
|
|
||||||
dbg!(&time);
|
|
||||||
dbg!(&date_time);
|
|
||||||
dbg!(&offset);
|
|
||||||
|
|
||||||
// let datetime_with_timezone = DateTime::from_naive_utc_and_offset(date_time, offset);
|
|
||||||
let date_time_fixed = match offset.from_local_datetime(&date_time).single() {
|
|
||||||
Some(d) => d,
|
|
||||||
None => {
|
|
||||||
return Value::error(
|
|
||||||
ShellError::IncorrectValue {
|
|
||||||
msg: "Ambiguous or invalid timezone conversion".to_string(),
|
|
||||||
val_span: head,
|
|
||||||
call_span: span,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Value::date(date_time_fixed, span)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||||
dbg!(input, args);
|
dbg!(input, args);
|
||||||
let timezone = &args.zone_options;
|
let timezone = &args.zone_options;
|
||||||
@ -729,6 +506,267 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = match parse_value_from_record_as_u32("nanosecond", 0, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let microsecond = match parse_value_from_record_as_u32("microsecond", 0, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let millisecond = match parse_value_from_record_as_u32("millisecond", 0, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let second = match parse_value_from_record_as_u32("second", 0, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let minute = match parse_value_from_record_as_u32("minute", 0, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let hour = match parse_value_from_record_as_u32("hour", 0, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let day = match parse_value_from_record_as_u32("day", 1, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let month = match parse_value_from_record_as_u32("month", 1, record, &head, &span) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(err) => {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let year: i32 = match record.get("year") {
|
||||||
|
Some(val) => match val {
|
||||||
|
Value::Int { val, .. } => *val as i32,
|
||||||
|
other => {
|
||||||
|
dbg!(other);
|
||||||
|
return Value::error(
|
||||||
|
ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "int".to_string(),
|
||||||
|
wrong_type: other.get_type().to_string(),
|
||||||
|
dst_span: head,
|
||||||
|
src_span: other.span(),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset = match record.get("timezone") {
|
||||||
|
Some(val) => match val {
|
||||||
|
Value::String { val, internal_span } => {
|
||||||
|
let offset: FixedOffset = match val.parse() {
|
||||||
|
Ok(offset) => offset,
|
||||||
|
Err(_) => {
|
||||||
|
return Value::error(
|
||||||
|
ShellError::IncorrectValue {
|
||||||
|
msg: "invalid timezone".to_string(),
|
||||||
|
val_span: head,
|
||||||
|
call_span: *internal_span,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
dbg!(other);
|
||||||
|
return Value::error(
|
||||||
|
ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "string".to_string(),
|
||||||
|
wrong_type: other.get_type().to_string(),
|
||||||
|
dst_span: head,
|
||||||
|
src_span: other.span(),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => FixedOffset::east_opt(0).unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
dbg!(&nanosecond);
|
||||||
|
dbg!(µsecond);
|
||||||
|
dbg!(&millisecond);
|
||||||
|
dbg!(&second);
|
||||||
|
dbg!(&minute);
|
||||||
|
dbg!(&hour);
|
||||||
|
dbg!(&day);
|
||||||
|
dbg!(&month);
|
||||||
|
dbg!(&year);
|
||||||
|
dbg!(&offset);
|
||||||
|
|
||||||
|
let total_nanoseconds = nanosecond + microsecond * 1_000 + millisecond * 1_000_000;
|
||||||
|
|
||||||
|
let date = match NaiveDate::from_ymd_opt(year, month, day) {
|
||||||
|
Some(d) => d,
|
||||||
|
None => {
|
||||||
|
return Value::error(
|
||||||
|
ShellError::IncorrectValue {
|
||||||
|
msg: "one of more values are incorrect and do not represent valid date"
|
||||||
|
.to_string(),
|
||||||
|
val_span: head,
|
||||||
|
call_span: span,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let time = match NaiveTime::from_hms_nano_opt(hour, minute, second, total_nanoseconds) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
return Value::error(
|
||||||
|
ShellError::IncorrectValue {
|
||||||
|
msg: "one of more values are incorrect and do not represent valid time"
|
||||||
|
.to_string(),
|
||||||
|
val_span: head,
|
||||||
|
call_span: span,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let date_time = NaiveDateTime::new(date, time);
|
||||||
|
|
||||||
|
dbg!(&date);
|
||||||
|
dbg!(&time);
|
||||||
|
dbg!(&date_time);
|
||||||
|
dbg!(&offset);
|
||||||
|
|
||||||
|
// let datetime_with_timezone = DateTime::from_naive_utc_and_offset(date_time, offset);
|
||||||
|
let date_time_fixed = match offset.from_local_datetime(&date_time).single() {
|
||||||
|
Some(d) => d,
|
||||||
|
None => {
|
||||||
|
return Value::error(
|
||||||
|
ShellError::IncorrectValue {
|
||||||
|
msg: "Ambiguous or invalid timezone conversion".to_string(),
|
||||||
|
val_span: head,
|
||||||
|
call_span: span,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Value::date(date_time_fixed, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_value_from_record_as_u32(
|
||||||
|
col: &str,
|
||||||
|
default: u32,
|
||||||
|
record: &Record,
|
||||||
|
head: &Span,
|
||||||
|
span: &Span,
|
||||||
|
) -> Result<u32, Value> {
|
||||||
|
let value: u32 = match record.get(col) {
|
||||||
|
Some(val) => match val {
|
||||||
|
Value::Int { val, .. } => {
|
||||||
|
if *val < 0 || *val > u32::MAX as i64 {
|
||||||
|
return Err(Value::error(
|
||||||
|
ShellError::IncorrectValue {
|
||||||
|
msg: format!("incorrect value for {}", col),
|
||||||
|
val_span: *head,
|
||||||
|
call_span: *span,
|
||||||
|
},
|
||||||
|
*span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
*val as u32
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
dbg!(other);
|
||||||
|
return Err(Value::error(
|
||||||
|
ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "int".to_string(),
|
||||||
|
wrong_type: other.get_type().to_string(),
|
||||||
|
dst_span: *head,
|
||||||
|
src_span: other.span(),
|
||||||
|
},
|
||||||
|
*span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => default,
|
||||||
|
};
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_human_readable_examples(span: Span) -> Value {
|
||||||
|
let examples: Vec<String> = vec![
|
||||||
|
"Today 18:30".into(),
|
||||||
|
"2022-11-07 13:25:30".into(),
|
||||||
|
"15:20 Friday".into(),
|
||||||
|
"This Friday 17:00".into(),
|
||||||
|
"13:25, Next Tuesday".into(),
|
||||||
|
"Last Friday at 19:45".into(),
|
||||||
|
"In 3 days".into(),
|
||||||
|
"In 2 hours".into(),
|
||||||
|
"10 hours and 5 minutes ago".into(),
|
||||||
|
"1 years ago".into(),
|
||||||
|
"A year ago".into(),
|
||||||
|
"A month ago".into(),
|
||||||
|
"A week ago".into(),
|
||||||
|
"A day ago".into(),
|
||||||
|
"An hour ago".into(),
|
||||||
|
"A minute ago".into(),
|
||||||
|
"A second ago".into(),
|
||||||
|
"Now".into(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let records = examples
|
||||||
|
.iter()
|
||||||
|
.map(|s| {
|
||||||
|
Value::record(
|
||||||
|
record! {
|
||||||
|
"parseable human datetime examples" => Value::test_string(s.to_string()),
|
||||||
|
"result" => action(&Value::test_string(s.to_string()), &Arguments { zone_options: None, format_options: None, cell_paths: None }, span)
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<Value>>();
|
||||||
|
|
||||||
|
Value::list(records, span)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -16,9 +16,9 @@ fn into_datetime_from_record() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn into_datetime_from_record_defaults() {
|
fn into_datetime_from_record_defaults() {
|
||||||
let actual = nu!(r#"{year: 2023, month: 1, day: 2} | into datetime | into record"#);
|
let actual = nu!(r#"{} | into datetime | into record"#);
|
||||||
let expected = nu!(
|
let expected = nu!(
|
||||||
r#"{year: 2023, month: 1, day: 2, hour: 0, minute: 0, second: 0, millisecond: 0, microsecond: 0, nanosecond: 0, timezone: '+00:00'}"#
|
r#"{year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 0, microsecond: 0, nanosecond: 0, timezone: '+00:00'}"#
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(expected.out, actual.out);
|
assert_eq!(expected.out, actual.out);
|
||||||
@ -67,8 +67,6 @@ fn into_datetime_from_record_fails_with_invalid_timezone() {
|
|||||||
|
|
||||||
// Tests invalid usage
|
// Tests invalid usage
|
||||||
|
|
||||||
// TODO: required key missing
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn into_datetime_from_record_fails_with_unknown_key() {
|
fn into_datetime_from_record_fails_with_unknown_key() {
|
||||||
let actual = nu!(r#"{year: 2023, unknown: 1} | into datetime"#);
|
let actual = nu!(r#"{year: 2023, unknown: 1} | into datetime"#);
|
||||||
|
Loading…
Reference in New Issue
Block a user