mirror of
https://github.com/nushell/nushell.git
synced 2025-07-16 06:15:07 +02:00
Move Value to helpers, separate span call (#10121)
# Description As part of the refactor to split spans off of Value, this moves to using helper functions to create values, and using `.span()` instead of matching span out of Value directly. Hoping to get a few more helping hands to finish this, as there are a lot of commands to update :) # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use std testing; testing run-tests --path crates/nu-std"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. --> --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com> Co-authored-by: WindSoilder <windsoilder@outlook.com>
This commit is contained in:
@ -86,53 +86,35 @@ impl Command for Fill {
|
||||
description:
|
||||
"Fill a string on the left side to a width of 15 with the character '─'",
|
||||
example: "'nushell' | fill -a l -c '─' -w 15",
|
||||
result: Some(Value::String {
|
||||
val: "nushell────────".into(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::string("nushell────────", Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Fill a string on the right side to a width of 15 with the character '─'",
|
||||
example: "'nushell' | fill -a r -c '─' -w 15",
|
||||
result: Some(Value::String {
|
||||
val: "────────nushell".into(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::string("────────nushell", Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description: "Fill a string on both sides to a width of 15 with the character '─'",
|
||||
example: "'nushell' | fill -a m -c '─' -w 15",
|
||||
result: Some(Value::String {
|
||||
val: "────nushell────".into(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::string("────nushell────", Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Fill a number on the left side to a width of 5 with the character '0'",
|
||||
example: "1 | fill --alignment right --character '0' --width 5",
|
||||
result: Some(Value::String {
|
||||
val: "00001".into(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::string("00001", Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description: "Fill a number on both sides to a width of 5 with the character '0'",
|
||||
example: "1.1 | fill --alignment center --character '0' --width 5",
|
||||
result: Some(Value::String {
|
||||
val: "01.10".into(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::string("01.10", Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Fill a filesize on the left side to a width of 5 with the character '0'",
|
||||
example: "1kib | fill --alignment middle --character '0' --width 10",
|
||||
result: Some(Value::String {
|
||||
val: "0001024000".into(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::string("0001024000", Span::test_data())),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -198,15 +180,15 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
Value::String { val, .. } => fill_string(val, args, span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "int, filesize, float, string".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,18 +196,18 @@ fn fill_float(num: f64, args: &Arguments, span: Span) -> Value {
|
||||
let s = num.to_string();
|
||||
let out_str = pad(&s, args.width, &args.character, args.alignment, false);
|
||||
|
||||
Value::String { val: out_str, span }
|
||||
Value::string(out_str, span)
|
||||
}
|
||||
fn fill_int(num: i64, args: &Arguments, span: Span) -> Value {
|
||||
let s = num.to_string();
|
||||
let out_str = pad(&s, args.width, &args.character, args.alignment, false);
|
||||
|
||||
Value::String { val: out_str, span }
|
||||
Value::string(out_str, span)
|
||||
}
|
||||
fn fill_string(s: &str, args: &Arguments, span: Span) -> Value {
|
||||
let out_str = pad(s, args.width, &args.character, args.alignment, false);
|
||||
|
||||
Value::String { val: out_str, span }
|
||||
Value::string(out_str, span)
|
||||
}
|
||||
|
||||
fn pad(s: &str, width: usize, pad_char: &str, alignment: FillAlignment, truncate: bool) -> String {
|
||||
|
@ -72,29 +72,29 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert string to a nushell binary primitive",
|
||||
example: "'This is a string that is exactly 52 characters long.' | into binary",
|
||||
result: Some(Value::Binary {
|
||||
val: "This is a string that is exactly 52 characters long."
|
||||
result: Some(Value::binary(
|
||||
"This is a string that is exactly 52 characters long."
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a number to a nushell binary primitive",
|
||||
example: "1 | into binary",
|
||||
result: Some(Value::Binary {
|
||||
val: i64::from(1).to_ne_bytes().to_vec(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::binary(
|
||||
i64::from(1).to_ne_bytes().to_vec(),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a boolean to a nushell binary primitive",
|
||||
example: "true | into binary",
|
||||
result: Some(Value::Binary {
|
||||
val: i64::from(1).to_ne_bytes().to_vec(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::binary(
|
||||
i64::from(1).to_ne_bytes().to_vec(),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a filesize to a nushell binary primitive",
|
||||
@ -109,19 +109,16 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert a decimal to a nushell binary primitive",
|
||||
example: "1.234 | into binary",
|
||||
result: Some(Value::Binary {
|
||||
val: 1.234f64.to_ne_bytes().to_vec(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::binary(
|
||||
1.234f64.to_ne_bytes().to_vec(),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"convert an integer to a nushell binary primitive with compact enabled",
|
||||
example: "10 | into binary --compact",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![10],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::binary(vec![10], Span::test_data())),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -138,22 +135,16 @@ fn into_binary(
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
|
||||
match input {
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
|
||||
val: vec![],
|
||||
span: head,
|
||||
PipelineData::ExternalStream { stdout: None, .. } => {
|
||||
Ok(Value::binary(vec![], head).into_pipeline_data())
|
||||
}
|
||||
.into_pipeline_data()),
|
||||
PipelineData::ExternalStream {
|
||||
stdout: Some(stream),
|
||||
..
|
||||
} => {
|
||||
// TODO: in the future, we may want this to stream out, converting each to bytes
|
||||
let output = stream.into_bytes()?;
|
||||
Ok(Value::Binary {
|
||||
val: output.item,
|
||||
span: head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
Ok(Value::binary(output.item, head).into_pipeline_data())
|
||||
}
|
||||
_ => {
|
||||
let args = Arguments {
|
||||
@ -168,50 +159,32 @@ fn into_binary(
|
||||
pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
||||
let value = match input {
|
||||
Value::Binary { .. } => input.clone(),
|
||||
Value::Int { val, .. } => Value::Binary {
|
||||
val: val.to_ne_bytes().to_vec(),
|
||||
span,
|
||||
},
|
||||
Value::Float { val, .. } => Value::Binary {
|
||||
val: val.to_ne_bytes().to_vec(),
|
||||
span,
|
||||
},
|
||||
Value::Filesize { val, .. } => Value::Binary {
|
||||
val: val.to_ne_bytes().to_vec(),
|
||||
span,
|
||||
},
|
||||
Value::String { val, .. } => Value::Binary {
|
||||
val: val.as_bytes().to_vec(),
|
||||
span,
|
||||
},
|
||||
Value::Bool { val, .. } => Value::Binary {
|
||||
val: i64::from(*val).to_ne_bytes().to_vec(),
|
||||
span,
|
||||
},
|
||||
Value::Duration { val, .. } => Value::Binary {
|
||||
val: val.to_ne_bytes().to_vec(),
|
||||
span,
|
||||
},
|
||||
Value::Date { val, .. } => Value::Binary {
|
||||
val: val.format("%c").to_string().as_bytes().to_vec(),
|
||||
span,
|
||||
},
|
||||
Value::Int { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
|
||||
Value::Float { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
|
||||
Value::Filesize { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
|
||||
Value::String { val, .. } => Value::binary(val.as_bytes().to_vec(), span),
|
||||
Value::Bool { val, .. } => Value::binary(i64::from(*val).to_ne_bytes().to_vec(), span),
|
||||
Value::Duration { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
|
||||
Value::Date { val, .. } => {
|
||||
Value::binary(val.format("%c").to_string().as_bytes().to_vec(), span)
|
||||
}
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "integer, float, filesize, string, date, duration, binary or bool"
|
||||
.into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
if _args.compact {
|
||||
if let Value::Binary { val, span } = value {
|
||||
let val_span = value.span();
|
||||
if let Value::Binary { val, .. } = value {
|
||||
let val = if cfg!(target_endian = "little") {
|
||||
match val.iter().rposition(|&x| x != 0) {
|
||||
Some(idx) => &val[..idx + 1],
|
||||
@ -224,10 +197,7 @@ pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
||||
}
|
||||
};
|
||||
|
||||
Value::Binary {
|
||||
val: val.to_vec(),
|
||||
span,
|
||||
}
|
||||
Value::binary(val.to_vec(), val_span)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Convert value to boolean in table",
|
||||
example: "[[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::bool(false, span)],
|
||||
@ -82,7 +82,7 @@ impl Command for SubCommand {
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert bool to boolean",
|
||||
@ -149,32 +149,23 @@ fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match input {
|
||||
Value::Bool { .. } => input.clone(),
|
||||
Value::Int { val, .. } => Value::Bool {
|
||||
val: *val != 0,
|
||||
span,
|
||||
},
|
||||
Value::Float { val, .. } => Value::Bool {
|
||||
val: val.abs() >= f64::EPSILON,
|
||||
span,
|
||||
},
|
||||
Value::Int { val, .. } => Value::bool(*val != 0, span),
|
||||
Value::Float { val, .. } => Value::bool(val.abs() >= f64::EPSILON, span),
|
||||
Value::String { val, .. } => match string_to_boolean(val, span) {
|
||||
Ok(val) => Value::Bool { val, span },
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
span,
|
||||
},
|
||||
Ok(val) => Value::bool(val, span),
|
||||
Err(error) => Value::error(error, span),
|
||||
},
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "bool, integer, float or string".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,16 +34,16 @@ impl Command for Into {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
Ok(Value::String {
|
||||
val: get_full_help(
|
||||
Ok(Value::string(
|
||||
get_full_help(
|
||||
&Into.signature(),
|
||||
&[],
|
||||
engine_state,
|
||||
stack,
|
||||
self.is_parser_keyword(),
|
||||
),
|
||||
span: call.head,
|
||||
}
|
||||
call.head,
|
||||
)
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
|
@ -154,10 +154,10 @@ impl Command for SubCommand {
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let example_result_1 = |nanos: i64| {
|
||||
Some(Value::Date {
|
||||
val: Utc.timestamp_nanos(nanos).into(),
|
||||
span: Span::test_data(),
|
||||
})
|
||||
Some(Value::date(
|
||||
Utc.timestamp_nanos(nanos).into(),
|
||||
Span::test_data(),
|
||||
))
|
||||
};
|
||||
vec![
|
||||
Example {
|
||||
@ -195,35 +195,35 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Convert list of timestamps to datetimes",
|
||||
example: r#"["2023-03-30 10:10:07 -05:00", "2023-05-05 13:43:49 -05:00", "2023-06-05 01:37:42 -05:00"] | into datetime"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Date {
|
||||
val: DateTime::parse_from_str(
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::date(
|
||||
DateTime::parse_from_str(
|
||||
"2023-03-30 10:10:07 -05:00",
|
||||
"%Y-%m-%d %H:%M:%S %z",
|
||||
)
|
||||
.expect("date calculation should not fail in test"),
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Date {
|
||||
val: DateTime::parse_from_str(
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::date(
|
||||
DateTime::parse_from_str(
|
||||
"2023-05-05 13:43:49 -05:00",
|
||||
"%Y-%m-%d %H:%M:%S %z",
|
||||
)
|
||||
.expect("date calculation should not fail in test"),
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Date {
|
||||
val: DateTime::parse_from_str(
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::date(
|
||||
DateTime::parse_from_str(
|
||||
"2023-06-05 01:37:42 -05:00",
|
||||
"%Y-%m-%d %H:%M:%S %z",
|
||||
)
|
||||
.expect("date calculation should not fail in test"),
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Span::test_data(),
|
||||
),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -240,12 +240,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
if matches!(input, Value::String { .. }) && dateformat.is_none() {
|
||||
if let Ok(input_val) = input.as_spanned_string() {
|
||||
match parse_date_from_string(&input_val.item, input_val.span) {
|
||||
Ok(date) => {
|
||||
return Value::Date {
|
||||
val: date,
|
||||
span: input_val.span,
|
||||
}
|
||||
}
|
||||
Ok(date) => return Value::date(date, input_val.span),
|
||||
Err(err) => err,
|
||||
};
|
||||
}
|
||||
@ -259,15 +254,15 @@ 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 => {
|
||||
return Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
return Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string and integer".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
span: head,
|
||||
};
|
||||
},
|
||||
head,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -277,107 +272,87 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
// note all these `.timestamp_nanos()` could overflow if we didn't check range in `<date> | into int`.
|
||||
|
||||
// default to UTC
|
||||
None => Value::Date {
|
||||
val: Utc.timestamp_nanos(ts).into(),
|
||||
span: head,
|
||||
},
|
||||
None => Value::date(Utc.timestamp_nanos(ts).into(), head),
|
||||
Some(Spanned { item, span }) => match item {
|
||||
Zone::Utc => {
|
||||
let dt = Utc.timestamp_nanos(ts);
|
||||
Value::Date {
|
||||
val: dt.into(),
|
||||
span: *span,
|
||||
}
|
||||
Value::date(dt.into(), *span)
|
||||
}
|
||||
Zone::Local => {
|
||||
let dt = Local.timestamp_nanos(ts);
|
||||
Value::Date {
|
||||
val: dt.into(),
|
||||
span: *span,
|
||||
}
|
||||
Value::date(dt.into(), *span)
|
||||
}
|
||||
Zone::East(i) => match FixedOffset::east_opt((*i as i32) * HOUR) {
|
||||
Some(eastoffset) => {
|
||||
let dt = eastoffset.timestamp_nanos(ts);
|
||||
Value::Date {
|
||||
val: dt,
|
||||
span: *span,
|
||||
}
|
||||
Value::date(dt, *span)
|
||||
}
|
||||
None => Value::Error {
|
||||
error: Box::new(ShellError::DatetimeParseError(
|
||||
input.debug_value(),
|
||||
*span,
|
||||
)),
|
||||
span: *span,
|
||||
},
|
||||
None => Value::error(
|
||||
ShellError::DatetimeParseError(input.debug_value(), *span),
|
||||
*span,
|
||||
),
|
||||
},
|
||||
Zone::West(i) => match FixedOffset::west_opt((*i as i32) * HOUR) {
|
||||
Some(westoffset) => {
|
||||
let dt = westoffset.timestamp_nanos(ts);
|
||||
Value::Date {
|
||||
val: dt,
|
||||
span: *span,
|
||||
}
|
||||
Value::date(dt, *span)
|
||||
}
|
||||
None => Value::Error {
|
||||
error: Box::new(ShellError::DatetimeParseError(
|
||||
input.debug_value(),
|
||||
*span,
|
||||
)),
|
||||
span: *span,
|
||||
},
|
||||
None => Value::error(
|
||||
ShellError::DatetimeParseError(input.debug_value(), *span),
|
||||
*span,
|
||||
),
|
||||
},
|
||||
Zone::Error => Value::Error {
|
||||
Zone::Error => Value::error(
|
||||
// This is an argument error, not an input error
|
||||
error: Box::new(ShellError::TypeMismatch {
|
||||
ShellError::TypeMismatch {
|
||||
err_message: "Invalid timezone or offset".to_string(),
|
||||
span: *span,
|
||||
}),
|
||||
span: *span,
|
||||
},
|
||||
},
|
||||
*span,
|
||||
),
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// If input is not a timestamp, try parsing it as a string
|
||||
let span = input.span();
|
||||
match input {
|
||||
Value::String { val, span } => {
|
||||
Value::String { val, .. } => {
|
||||
match dateformat {
|
||||
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
|
||||
Ok(d) => Value::Date { val: d, span: head },
|
||||
Ok(d) => Value::date ( d, head ),
|
||||
Err(reason) => {
|
||||
Value::Error {
|
||||
error: Box::new(ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) }),
|
||||
span: head,
|
||||
}
|
||||
Value::error (
|
||||
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
|
||||
head,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
// Tries to automatically parse the date
|
||||
// (i.e. without a format string)
|
||||
// and assumes the system's local timezone if none is specified
|
||||
None => match parse_date_from_string(val, *span) {
|
||||
Ok(date) => Value::Date {
|
||||
val: date,
|
||||
span: *span,
|
||||
},
|
||||
None => match parse_date_from_string(val, span) {
|
||||
Ok(date) => Value::date (
|
||||
date,
|
||||
span,
|
||||
),
|
||||
Err(err) => err,
|
||||
},
|
||||
}
|
||||
}
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
span: head,
|
||||
},
|
||||
},
|
||||
head,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,11 +379,10 @@ mod tests {
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")
|
||||
.unwrap(),
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let expected = Value::date(
|
||||
DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z").unwrap(),
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
@ -421,11 +395,10 @@ mod tests {
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z")
|
||||
.unwrap(),
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let expected = Value::date(
|
||||
DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z").unwrap(),
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
@ -442,11 +415,10 @@ mod tests {
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
.unwrap(),
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let expected = Value::date(
|
||||
DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z").unwrap(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
@ -464,11 +436,10 @@ mod tests {
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_int, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
.unwrap(),
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let expected = Value::date(
|
||||
DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z").unwrap(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
@ -486,10 +457,10 @@ mod tests {
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: Local.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let expected = Value::date(
|
||||
Local.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
@ -504,10 +475,10 @@ mod tests {
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
|
||||
let expected = Value::Date {
|
||||
val: Utc.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||
span: Span::test_data(),
|
||||
};
|
||||
let expected = Value::date(
|
||||
Utc.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
@ -62,13 +62,13 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Convert string to decimal in table",
|
||||
example: "[[num]; ['5.01']] | into decimal num",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_record(Record {
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["num".to_string()],
|
||||
vals: vec![Value::test_float(5.01)],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to decimal",
|
||||
@ -93,43 +93,44 @@ impl Command for SubCommand {
|
||||
}
|
||||
|
||||
fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
|
||||
let span = input.span();
|
||||
match input {
|
||||
Value::Float { .. } => input.clone(),
|
||||
Value::String { val: s, span } => {
|
||||
Value::String { val: s, .. } => {
|
||||
let other = s.trim();
|
||||
|
||||
match other.parse::<f64>() {
|
||||
Ok(x) => Value::float(x, head),
|
||||
Err(reason) => Value::Error {
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
Err(reason) => Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: "float".to_string(),
|
||||
from_type: reason.to_string(),
|
||||
span: *span,
|
||||
span,
|
||||
help: None,
|
||||
}),
|
||||
span: *span,
|
||||
},
|
||||
},
|
||||
span,
|
||||
),
|
||||
}
|
||||
}
|
||||
Value::Int { val: v, span } => Value::float(*v as f64, *span),
|
||||
Value::Bool { val: b, span } => Value::Float {
|
||||
val: match b {
|
||||
Value::Int { val: v, .. } => Value::float(*v as f64, span),
|
||||
Value::Bool { val: b, .. } => Value::float(
|
||||
match b {
|
||||
true => 1.0,
|
||||
false => 0.0,
|
||||
},
|
||||
span: *span,
|
||||
},
|
||||
span,
|
||||
),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string, integer or bool".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
span: head,
|
||||
},
|
||||
},
|
||||
head,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,71 +62,50 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Convert duration string to duration value",
|
||||
example: "'7min' | into duration",
|
||||
result: Some(Value::Duration {
|
||||
val: 7 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}),
|
||||
result: Some(Value::duration(7 * 60 * NS_PER_SEC, span)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert compound duration string to duration value",
|
||||
example: "'1day 2hr 3min 4sec' | into duration",
|
||||
result: Some(Value::Duration {
|
||||
val: (((((/* 1 * */24) + 2) * 60) + 3) * 60 + 4) * NS_PER_SEC,
|
||||
result: Some(Value::duration(
|
||||
(((((/* 1 * */24) + 2) * 60) + 3) * 60 + 4) * NS_PER_SEC,
|
||||
span,
|
||||
}),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert table of duration strings to table of duration values",
|
||||
example:
|
||||
"[[value]; ['1sec'] ['2min'] ['3hr'] ['4day'] ['5wk']] | into duration value",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
vals: vec![Value::duration(NS_PER_SEC, span)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 2 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
vals: vec![Value::duration(2 * 60 * NS_PER_SEC, span)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 3 * 60 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
vals: vec![Value::duration(3 * 60 * 60 * NS_PER_SEC, span)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 4 * 24 * 60 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
vals: vec![Value::duration(4 * 24 * 60 * 60 * NS_PER_SEC, span)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["value".to_string()],
|
||||
vals: vec![Value::Duration {
|
||||
val: 5 * 7 * 24 * 60 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}],
|
||||
vals: vec![Value::duration(5 * 7 * 24 * 60 * 60 * NS_PER_SEC, span)],
|
||||
}),
|
||||
],
|
||||
span,
|
||||
}),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert duration to duration",
|
||||
example: "420sec | into duration",
|
||||
result: Some(Value::Duration {
|
||||
val: 7 * 60 * NS_PER_SEC,
|
||||
span,
|
||||
}),
|
||||
result: Some(Value::duration(7 * 60 * NS_PER_SEC, span)),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -154,10 +133,7 @@ fn into_duration(
|
||||
let r =
|
||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, span)));
|
||||
if let Err(error) = r {
|
||||
return Value::Error {
|
||||
error: Box::new(error),
|
||||
span,
|
||||
};
|
||||
return Value::error(error, span);
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,29 +203,24 @@ fn string_to_duration(s: &str, span: Span) -> Result<i64, ShellError> {
|
||||
}
|
||||
|
||||
fn action(input: &Value, span: Span) -> Value {
|
||||
let value_span = input.span();
|
||||
match input {
|
||||
Value::Duration { .. } => input.clone(),
|
||||
Value::String {
|
||||
val,
|
||||
span: value_span,
|
||||
} => match compound_to_duration(val, *value_span) {
|
||||
Ok(val) => Value::Duration { val, span },
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
span,
|
||||
},
|
||||
Value::String { val, .. } => match compound_to_duration(val, value_span) {
|
||||
Ok(val) => Value::duration(val, span),
|
||||
Err(error) => Value::error(error, span),
|
||||
},
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string or duration".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,69 +79,45 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Convert string to filesize in table",
|
||||
example: r#"[[device size]; ["/dev/sda1" "200"] ["/dev/loop0" "50"]] | into filesize size"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["device".to_string(), "size".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "/dev/sda1".to_string(),
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Filesize {
|
||||
val: 200,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::string("/dev/sda1".to_string(), Span::test_data()),
|
||||
Value::filesize(200, Span::test_data()),
|
||||
],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["device".to_string(), "size".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "/dev/loop0".to_string(),
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Filesize {
|
||||
val: 50,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::string("/dev/loop0".to_string(), Span::test_data()),
|
||||
Value::filesize(50, Span::test_data()),
|
||||
],
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to filesize",
|
||||
example: "'2' | into filesize",
|
||||
result: Some(Value::Filesize {
|
||||
val: 2,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::filesize(2, Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal to filesize",
|
||||
example: "8.3 | into filesize",
|
||||
result: Some(Value::Filesize {
|
||||
val: 8,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::filesize(8, Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description: "Convert int to filesize",
|
||||
example: "5 | into filesize",
|
||||
result: Some(Value::Filesize {
|
||||
val: 5,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::filesize(5, Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description: "Convert file size to filesize",
|
||||
example: "4KB | into filesize",
|
||||
result: Some(Value::Filesize {
|
||||
val: 4000,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::filesize(4000, Span::test_data())),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -151,37 +127,22 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
let value_span = input.span();
|
||||
match input {
|
||||
Value::Filesize { .. } => input.clone(),
|
||||
Value::Int { val, .. } => Value::Filesize {
|
||||
val: *val,
|
||||
span: value_span,
|
||||
},
|
||||
Value::Float { val, .. } => Value::Filesize {
|
||||
val: *val as i64,
|
||||
span: value_span,
|
||||
},
|
||||
Value::Int { val, .. } => Value::filesize(*val, value_span),
|
||||
Value::Float { val, .. } => Value::filesize(*val as i64, value_span),
|
||||
Value::String { val, .. } => match int_from_string(val, value_span) {
|
||||
Ok(val) => Value::Filesize {
|
||||
val,
|
||||
span: value_span,
|
||||
},
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
span: value_span,
|
||||
},
|
||||
Ok(val) => Value::filesize(val, value_span),
|
||||
Err(error) => Value::error(error, value_span),
|
||||
},
|
||||
Value::Nothing { .. } => Value::Filesize {
|
||||
val: 0,
|
||||
span: value_span,
|
||||
},
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
Value::Nothing { .. } => Value::filesize(0, value_span),
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string and integer".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: value_span,
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {
|
||||
|
@ -107,33 +107,44 @@ impl Command for SubCommand {
|
||||
|
||||
let radix = call.get_flag::<Value>(engine_state, stack, "radix")?;
|
||||
let radix: u32 = match radix {
|
||||
Some(Value::Int { val, span }) => {
|
||||
if !(2..=36).contains(&val) {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "Radix must lie in the range [2, 36]".to_string(),
|
||||
span,
|
||||
});
|
||||
Some(val) => {
|
||||
let span = val.span();
|
||||
match val {
|
||||
Value::Int { val, .. } => {
|
||||
if !(2..=36).contains(&val) {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "Radix must lie in the range [2, 36]".to_string(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
val as u32
|
||||
}
|
||||
_ => 10,
|
||||
}
|
||||
val as u32
|
||||
}
|
||||
Some(_) => 10,
|
||||
None => 10,
|
||||
};
|
||||
|
||||
let endian = call.get_flag::<Value>(engine_state, stack, "endian")?;
|
||||
let little_endian = match endian {
|
||||
Some(Value::String { val, span }) => match val.as_str() {
|
||||
"native" => cfg!(target_endian = "little"),
|
||||
"little" => true,
|
||||
"big" => false,
|
||||
_ => {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "Endian must be one of native, little, big".to_string(),
|
||||
span,
|
||||
})
|
||||
Some(val) => {
|
||||
let span = val.span();
|
||||
match val {
|
||||
Value::String { val, .. } => match val.as_str() {
|
||||
"native" => cfg!(target_endian = "little"),
|
||||
"little" => true,
|
||||
"big" => false,
|
||||
_ => {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "Endian must be one of native, little, big"
|
||||
.to_string(),
|
||||
span,
|
||||
})
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
},
|
||||
Some(_) => false,
|
||||
}
|
||||
None => cfg!(target_endian = "little"),
|
||||
};
|
||||
|
||||
@ -175,10 +186,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Convert bool to integer",
|
||||
example: "[false, true] | into int",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_int(0), Value::test_int(1)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_int(0), Value::test_int(1)],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert date to integer (Unix nanosecond timestamp)",
|
||||
@ -217,6 +228,7 @@ impl Command for SubCommand {
|
||||
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
let radix = args.radix;
|
||||
let little_endian = args.little_endian;
|
||||
let val_span = input.span();
|
||||
match input {
|
||||
Value::Int { val: _, .. } => {
|
||||
if radix == 10 {
|
||||
@ -225,47 +237,35 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
convert_int(input, span, radix)
|
||||
}
|
||||
}
|
||||
Value::Filesize { val, .. } => Value::Int { val: *val, span },
|
||||
Value::Float { val, .. } => Value::Int {
|
||||
val: {
|
||||
Value::Filesize { val, .. } => Value::int(*val, span),
|
||||
Value::Float { val, .. } => Value::int(
|
||||
{
|
||||
if radix == 10 {
|
||||
*val as i64
|
||||
} else {
|
||||
match convert_int(
|
||||
&Value::Int {
|
||||
val: *val as i64,
|
||||
span,
|
||||
},
|
||||
span,
|
||||
radix,
|
||||
)
|
||||
.as_i64()
|
||||
{
|
||||
match convert_int(&Value::int(*val as i64, span), span, radix).as_i64() {
|
||||
Ok(v) => v,
|
||||
_ => {
|
||||
return Value::Error {
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
return Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: "float".to_string(),
|
||||
from_type: "integer".to_string(),
|
||||
span,
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
Value::String { val, .. } => {
|
||||
if radix == 10 {
|
||||
match int_from_string(val, span) {
|
||||
Ok(val) => Value::Int { val, span },
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
span,
|
||||
},
|
||||
Ok(val) => Value::int(val, span),
|
||||
Err(error) => Value::error(error, span),
|
||||
}
|
||||
} else {
|
||||
convert_int(input, span, radix)
|
||||
@ -273,15 +273,12 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
}
|
||||
Value::Bool { val, .. } => {
|
||||
if *val {
|
||||
Value::Int { val: 1, span }
|
||||
Value::int(1, span)
|
||||
} else {
|
||||
Value::Int { val: 0, span }
|
||||
Value::int(0, span)
|
||||
}
|
||||
}
|
||||
Value::Date {
|
||||
val,
|
||||
span: val_span,
|
||||
} => {
|
||||
Value::Date { val, .. } => {
|
||||
if val
|
||||
< &FixedOffset::east_opt(0)
|
||||
.expect("constant")
|
||||
@ -293,23 +290,20 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
.with_ymd_and_hms(2262, 4, 11, 23, 47, 16)
|
||||
.unwrap()
|
||||
{
|
||||
Value::Error {
|
||||
error: Box::new(ShellError::IncorrectValue {
|
||||
Value::error (
|
||||
ShellError::IncorrectValue {
|
||||
msg: "DateTime out of range for timestamp: 1677-09-21T00:12:43Z to 2262-04-11T23:47:16".to_string(),
|
||||
val_span: *val_span,
|
||||
val_span,
|
||||
call_span: span,
|
||||
}),
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Value::Int {
|
||||
val: val.timestamp_nanos(),
|
||||
span,
|
||||
}
|
||||
Value::int(val.timestamp_nanos(), span)
|
||||
}
|
||||
}
|
||||
Value::Duration { val, .. } => Value::Int { val: *val, span },
|
||||
Value::Binary { val, span } => {
|
||||
Value::Duration { val, .. } => Value::int(*val, span),
|
||||
Value::Binary { val, .. } => {
|
||||
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||
|
||||
let mut val = val.to_vec();
|
||||
@ -320,28 +314,28 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
}
|
||||
val.resize(8, 0);
|
||||
|
||||
Value::int(LittleEndian::read_i64(&val), *span)
|
||||
Value::int(LittleEndian::read_i64(&val), val_span)
|
||||
} else {
|
||||
while val.len() < 8 {
|
||||
val.insert(0, 0);
|
||||
}
|
||||
val.resize(8, 0);
|
||||
|
||||
Value::int(BigEndian::read_i64(&val), *span)
|
||||
Value::int(BigEndian::read_i64(&val), val_span)
|
||||
}
|
||||
}
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "integer, float, filesize, date, string, binary, duration or bool"
|
||||
.into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,27 +351,22 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
|
||||
{
|
||||
match int_from_string(val, head) {
|
||||
Ok(x) => return Value::int(x, head),
|
||||
Err(e) => {
|
||||
return Value::Error {
|
||||
error: Box::new(e),
|
||||
span: head,
|
||||
}
|
||||
}
|
||||
Err(e) => return Value::error(e, head),
|
||||
}
|
||||
} else if val.starts_with("00") {
|
||||
// It's a padded string
|
||||
match i64::from_str_radix(val, radix) {
|
||||
Ok(n) => return Value::int(n, head),
|
||||
Err(e) => {
|
||||
return Value::Error {
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
return Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: "string".to_string(),
|
||||
from_type: "int".to_string(),
|
||||
span: head,
|
||||
help: Some(e.to_string()),
|
||||
}),
|
||||
span: head,
|
||||
}
|
||||
},
|
||||
head,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -386,28 +375,28 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => return input.clone(),
|
||||
other => {
|
||||
return Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
return Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string and integer".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
span: head,
|
||||
};
|
||||
},
|
||||
head,
|
||||
);
|
||||
}
|
||||
};
|
||||
match i64::from_str_radix(i.trim(), radix) {
|
||||
Ok(n) => Value::int(n, head),
|
||||
Err(_reason) => Value::Error {
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
Err(_reason) => Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: "string".to_string(),
|
||||
from_type: "int".to_string(),
|
||||
span: head,
|
||||
help: None,
|
||||
}),
|
||||
span: head,
|
||||
},
|
||||
},
|
||||
head,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,9 +62,9 @@ impl Command for SubCommand {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
|
||||
vals: vec![
|
||||
Value::Int { val: 1, span },
|
||||
Value::Int { val: 2, span },
|
||||
Value::Int { val: 3, span },
|
||||
Value::int(1, span),
|
||||
Value::int(2, span),
|
||||
Value::int(3, span),
|
||||
],
|
||||
})),
|
||||
},
|
||||
@ -74,9 +74,9 @@ impl Command for SubCommand {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
|
||||
vals: vec![
|
||||
Value::Int { val: 0, span },
|
||||
Value::Int { val: 1, span },
|
||||
Value::Int { val: 2, span },
|
||||
Value::int(0, span),
|
||||
Value::int(1, span),
|
||||
Value::int(2, span),
|
||||
],
|
||||
})),
|
||||
},
|
||||
@ -92,14 +92,11 @@ impl Command for SubCommand {
|
||||
"sign".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::Int { val: 71, span },
|
||||
Value::Int { val: 3, span },
|
||||
Value::Int { val: 4, span },
|
||||
Value::Int { val: 5, span },
|
||||
Value::String {
|
||||
val: "-".into(),
|
||||
span,
|
||||
},
|
||||
Value::int(71, span),
|
||||
Value::int(3, span),
|
||||
Value::int(4, span),
|
||||
Value::int(5, span),
|
||||
Value::string("-", span),
|
||||
],
|
||||
})),
|
||||
},
|
||||
@ -108,7 +105,7 @@ impl Command for SubCommand {
|
||||
example: "{a: 1, b: 2} | into record",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::Int { val: 1, span }, Value::Int { val: 2, span }],
|
||||
vals: vec![Value::int(1, span), Value::int(2, span)],
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
@ -125,16 +122,13 @@ impl Command for SubCommand {
|
||||
"timezone".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::Int { val: 2020, span },
|
||||
Value::Int { val: 4, span },
|
||||
Value::Int { val: 12, span },
|
||||
Value::Int { val: 22, span },
|
||||
Value::Int { val: 10, span },
|
||||
Value::Int { val: 57, span },
|
||||
Value::String {
|
||||
val: "+02:00".to_string(),
|
||||
span,
|
||||
},
|
||||
Value::int(2020, span),
|
||||
Value::int(4, span),
|
||||
Value::int(12, span),
|
||||
Value::int(22, span),
|
||||
Value::int(10, span),
|
||||
Value::int(57, span),
|
||||
Value::string("+02:00".to_string(), span),
|
||||
],
|
||||
})),
|
||||
},
|
||||
@ -149,10 +143,11 @@ fn into_record(
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let input = input.into_value(call.head);
|
||||
let input_type = input.get_type();
|
||||
let span = input.span();
|
||||
let res = match input {
|
||||
Value::Date { val, span } => parse_date_into_record(val, span),
|
||||
Value::Duration { val, span } => parse_duration_into_record(val, span),
|
||||
Value::List { mut vals, span } => match input_type {
|
||||
Value::Date { val, .. } => parse_date_into_record(val, span),
|
||||
Value::Duration { val, .. } => parse_duration_into_record(val, span),
|
||||
Value::List { mut vals, .. } => match input_type {
|
||||
Type::Table(..) if vals.len() == 1 => vals.pop().expect("already checked 1 item"),
|
||||
_ => Value::record(
|
||||
vals.into_iter()
|
||||
@ -162,24 +157,24 @@ fn into_record(
|
||||
span,
|
||||
),
|
||||
},
|
||||
Value::Range { val, span } => Value::record(
|
||||
Value::Range { val, .. } => Value::record(
|
||||
val.into_range_iter(engine_state.ctrlc.clone())?
|
||||
.enumerate()
|
||||
.map(|(idx, val)| (format!("{idx}"), val))
|
||||
.collect(),
|
||||
span,
|
||||
),
|
||||
Value::Record { val, span } => Value::Record { val, span },
|
||||
Value::Record { val, .. } => Value::record(val, span),
|
||||
Value::Error { .. } => input,
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: call.head,
|
||||
src_span: other.span(),
|
||||
}),
|
||||
span: call.head,
|
||||
},
|
||||
},
|
||||
call.head,
|
||||
),
|
||||
};
|
||||
Ok(res.into_pipeline_data())
|
||||
}
|
||||
|
@ -182,22 +182,16 @@ fn string_helper(
|
||||
};
|
||||
|
||||
match input {
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
|
||||
val: String::new(),
|
||||
span: head,
|
||||
PipelineData::ExternalStream { stdout: None, .. } => {
|
||||
Ok(Value::string(String::new(), head).into_pipeline_data())
|
||||
}
|
||||
.into_pipeline_data()),
|
||||
PipelineData::ExternalStream {
|
||||
stdout: Some(stream),
|
||||
..
|
||||
} => {
|
||||
// TODO: in the future, we may want this to stream out, converting each to bytes
|
||||
let output = stream.into_string()?;
|
||||
Ok(Value::String {
|
||||
val: output.item,
|
||||
span: head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
Ok(Value::string(output.item, head).into_pipeline_data())
|
||||
}
|
||||
_ => operate(action, args, input, head, engine_state.ctrlc.clone()),
|
||||
}
|
||||
@ -211,80 +205,53 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
Value::Int { val, .. } => {
|
||||
let decimal_value = digits.unwrap_or(0) as usize;
|
||||
let res = format_int(*val, false, decimal_value);
|
||||
Value::String { val: res, span }
|
||||
Value::string(res, span)
|
||||
}
|
||||
Value::Float { val, .. } => {
|
||||
if decimals {
|
||||
let decimal_value = digits.unwrap_or(2) as usize;
|
||||
Value::String {
|
||||
val: format!("{val:.decimal_value$}"),
|
||||
span,
|
||||
}
|
||||
Value::string(format!("{val:.decimal_value$}"), span)
|
||||
} else {
|
||||
Value::String {
|
||||
val: val.to_string(),
|
||||
span,
|
||||
}
|
||||
Value::string(val.to_string(), span)
|
||||
}
|
||||
}
|
||||
Value::Bool { val, .. } => Value::String {
|
||||
val: val.to_string(),
|
||||
span,
|
||||
},
|
||||
Value::Date { val, .. } => Value::String {
|
||||
val: val.format("%c").to_string(),
|
||||
span,
|
||||
},
|
||||
Value::String { val, .. } => Value::String {
|
||||
val: val.to_string(),
|
||||
span,
|
||||
},
|
||||
Value::Bool { val, .. } => Value::string(val.to_string(), span),
|
||||
Value::Date { val, .. } => Value::string(val.format("%c").to_string(), span),
|
||||
Value::String { val, .. } => Value::string(val.to_string(), span),
|
||||
|
||||
Value::Filesize { val: _, .. } => Value::String {
|
||||
val: input.into_string(", ", config),
|
||||
span,
|
||||
},
|
||||
Value::Duration { val: _, .. } => Value::String {
|
||||
val: input.into_string("", config),
|
||||
span,
|
||||
},
|
||||
Value::Filesize { val: _, .. } => Value::string(input.into_string(", ", config), span),
|
||||
Value::Duration { val: _, .. } => Value::string(input.into_string("", config), span),
|
||||
|
||||
Value::Error { error, .. } => Value::String {
|
||||
val: into_code(error).unwrap_or_default(),
|
||||
span,
|
||||
},
|
||||
Value::Nothing { .. } => Value::String {
|
||||
val: "".to_string(),
|
||||
span,
|
||||
},
|
||||
Value::Record { .. } => Value::Error {
|
||||
Value::Error { error, .. } => Value::string(into_code(error).unwrap_or_default(), span),
|
||||
Value::Nothing { .. } => Value::string("".to_string(), span),
|
||||
Value::Record { .. } => Value::error(
|
||||
// Watch out for CantConvert's argument order
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
ShellError::CantConvert {
|
||||
to_type: "string".into(),
|
||||
from_type: "record".into(),
|
||||
span,
|
||||
help: Some("try using the `to nuon` command".into()),
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
Value::Binary { .. } => Value::Error {
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
),
|
||||
Value::Binary { .. } => Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: "string".into(),
|
||||
from_type: "binary".into(),
|
||||
span,
|
||||
help: Some("try using the `decode` command".into()),
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
x => Value::Error {
|
||||
error: Box::new(ShellError::CantConvert {
|
||||
),
|
||||
x => Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: String::from("string"),
|
||||
from_type: x.get_type().to_string(),
|
||||
span,
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
span,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user