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:
JT
2023-09-04 02:27:29 +12:00
committed by GitHub
parent af79eb2943
commit 6cdfee3573
372 changed files with 5811 additions and 7448 deletions

View File

@ -91,56 +91,50 @@ impl Command for BytesAdd {
Example {
description: "Add bytes `0x[AA]` to `0x[1F FF AA AA]`",
example: "0x[1F FF AA AA] | bytes add 0x[AA]",
result: Some(Value::Binary {
val: vec![0xAA, 0x1F, 0xFF, 0xAA, 0xAA],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0xAA, 0x1F, 0xFF, 0xAA, 0xAA],
Span::test_data(),
)),
},
Example {
description: "Add bytes `0x[AA BB]` to `0x[1F FF AA AA]` at index 1",
example: "0x[1F FF AA AA] | bytes add 0x[AA BB] -i 1",
result: Some(Value::Binary {
val: vec![0x1F, 0xAA, 0xBB, 0xFF, 0xAA, 0xAA],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0x1F, 0xAA, 0xBB, 0xFF, 0xAA, 0xAA],
Span::test_data(),
)),
},
Example {
description: "Add bytes `0x[11]` to `0x[FF AA AA]` at the end",
example: "0x[FF AA AA] | bytes add 0x[11] -e",
result: Some(Value::Binary {
val: vec![0xFF, 0xAA, 0xAA, 0x11],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0xFF, 0xAA, 0xAA, 0x11],
Span::test_data(),
)),
},
Example {
description: "Add bytes `0x[11 22 33]` to `0x[FF AA AA]` at the end, at index 1(the index is start from end)",
example: "0x[FF AA BB] | bytes add 0x[11 22 33] -e -i 1",
result: Some(Value::Binary {
val: vec![0xFF, 0xAA, 0x11, 0x22, 0x33, 0xBB],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0xFF, 0xAA, 0x11, 0x22, 0x33, 0xBB],
Span::test_data(),
)),
},
]
}
}
fn add(val: &Value, args: &Arguments, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => add_impl(val, args, *val_span),
Value::Binary { val, .. } => add_impl(val, args, val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}
@ -151,12 +145,12 @@ fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
let mut added_data = args.added_data.clone();
let mut result = input.to_vec();
result.append(&mut added_data);
Value::Binary { val: result, span }
Value::binary(result, span)
} else {
let mut result = args.added_data.clone();
let mut input = input.to_vec();
result.append(&mut input);
Value::Binary { val: result, span }
Value::binary(result, span)
}
}
Some(mut indx) => {
@ -175,7 +169,7 @@ fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
result.append(&mut added_data);
let mut after_data = input[inserted_index..].to_vec();
result.append(&mut after_data);
Value::Binary { val: result, span }
Value::binary(result, span)
}
}
}

View File

@ -94,18 +94,12 @@ impl Command for BytesAt {
Example {
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
example: " 0x[33 44 55 10 01 13] | bytes at 3..<4",
result: Some(Value::Binary {
val: vec![0x10],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0x10], Span::test_data())),
},
Example {
description: "Get a subbytes `0x[10 01 13]` from the bytes `0x[33 44 55 10 01 13]`",
example: " 0x[33 44 55 10 01 13] | bytes at 3..6",
result: Some(Value::Binary {
val: vec![0x10, 0x01, 0x13],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0x10, 0x01, 0x13], Span::test_data())),
},
Example {
description: "Get the remaining characters from a starting index",
@ -118,35 +112,26 @@ impl Command for BytesAt {
Example {
description: "Get the characters from the beginning until ending index",
example: " 0x[33 44 55 10 01 13] | bytes at ..<4",
result: Some(Value::Binary {
val: vec![0x33, 0x44, 0x55, 0x10],
span: Span::test_data(),
}),
result: Some(Value::binary(
vec![0x33, 0x44, 0x55, 0x10],
Span::test_data(),
)),
},
Example {
description:
"Or the characters from the beginning until ending index inside a table",
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
vals: vec![
Value::Binary {
val: vec![0x11, 0x12, 0x13],
span: Span::test_data(),
},
Value::Binary {
val: vec![0x15, 0x16],
span: Span::test_data(),
},
Value::Binary {
val: vec![0x18, 0x19],
span: Span::test_data(),
},
Value::binary(vec![0x11, 0x12, 0x13], Span::test_data()),
Value::binary(vec![0x15, 0x16], Span::test_data()),
Value::binary(vec![0x18, 0x19], Span::test_data()),
],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -169,51 +154,46 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
if start < len && end >= 0 {
match start.cmp(&end) {
Ordering::Equal => Value::Binary {
val: vec![],
span: head,
},
Ordering::Greater => Value::Error {
error: Box::new(ShellError::TypeMismatch {
Ordering::Equal => Value::binary(vec![], head),
Ordering::Greater => Value::error(
ShellError::TypeMismatch {
err_message: "End must be greater than or equal to Start".to_string(),
span: head,
}),
span: head,
},
Ordering::Less => Value::Binary {
val: {
if end == isize::max_value() {
val.iter().skip(start as usize).copied().collect()
} else {
val.iter()
.skip(start as usize)
.take((end - start) as usize)
.copied()
.collect()
}
},
span: head,
},
head,
),
Ordering::Less => Value::binary(
if end == isize::max_value() {
val.iter()
.skip(start as usize)
.copied()
.collect::<Vec<u8>>()
} else {
val.iter()
.skip(start as usize)
.take((end - start) as usize)
.copied()
.collect()
},
head,
),
}
} else {
Value::Binary {
val: vec![],
span: head,
}
Value::binary(vec![], head)
}
}
Value::Error { .. } => input.clone(),
other => Value::Error {
error: Box::new(ShellError::UnsupportedInput(
other => Value::error(
ShellError::UnsupportedInput(
"Only binary values are supported".into(),
format!("input type: {:?}", other.get_type()),
head,
// This line requires the Value::Error match above.
other.span(),
)),
span: head,
},
),
head,
),
}
}

View File

@ -33,10 +33,10 @@ impl Command for BytesBuild {
vec![Example {
example: "bytes build 0x[01 02] 0x[03] 0x[04]",
description: "Builds binary data from 0x[01 02], 0x[03], 0x[04]",
result: Some(Value::Binary {
val: vec![0x01, 0x02, 0x03, 0x04],
span: Span::test_data(),
}),
result: Some(Value::binary(
vec![0x01, 0x02, 0x03, 0x04],
Span::test_data(),
)),
}]
}
@ -63,11 +63,7 @@ impl Command for BytesBuild {
}
}
Ok(Value::Binary {
val: output,
span: call.head,
}
.into_pipeline_data())
Ok(Value::binary(output, call.head).into_pipeline_data())
}
}

View File

@ -34,16 +34,16 @@ impl Command for Bytes {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::String {
val: get_full_help(
Ok(Value::string(
get_full_help(
&Bytes.signature(),
&Bytes.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head,
}
call.head,
)
.into_pipeline_data())
}
}

View File

@ -68,28 +68,16 @@ impl Command for BytesCollect {
}
match separator {
None => Ok(Value::Binary {
val: output_binary,
span: call.head,
}
.into_pipeline_data()),
None => Ok(Value::binary(output_binary, call.head).into_pipeline_data()),
Some(sep) => {
if output_binary.is_empty() {
Ok(Value::Binary {
val: output_binary,
span: call.head,
}
.into_pipeline_data())
Ok(Value::binary(output_binary, call.head).into_pipeline_data())
} else {
// have push one extra separator in previous step, pop them out.
for _ in sep {
let _ = output_binary.pop();
}
Ok(Value::Binary {
val: output_binary,
span: call.head,
}
.into_pipeline_data())
Ok(Value::binary(output_binary, call.head).into_pipeline_data())
}
}
}
@ -100,18 +88,15 @@ impl Command for BytesCollect {
Example {
description: "Create a byte array from input",
example: "[0x[11] 0x[13 15]] | bytes collect",
result: Some(Value::Binary {
val: vec![0x11, 0x13, 0x15],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0x11, 0x13, 0x15], Span::test_data())),
},
Example {
description: "Create a byte array from input with a separator",
example: "[0x[11] 0x[33] 0x[44]] | bytes collect 0x[01]",
result: Some(Value::Binary {
val: vec![0x11, 0x01, 0x33, 0x01, 0x44],
span: Span::test_data(),
}),
result: Some(Value::binary(
vec![0x11, 0x01, 0x33, 0x01, 0x44],
Span::test_data(),
)),
},
]
}

View File

@ -89,22 +89,20 @@ impl Command for BytesEndsWith {
}
fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => Value::bool(val.ends_with(&args.pattern), *val_span),
Value::Binary { val, .. } => Value::bool(val.ends_with(&args.pattern), val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}

View File

@ -95,58 +95,53 @@ impl Command for BytesIndexOf {
Example {
description: "Returns all matched index",
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of -a 0x[33 44]",
result: Some(Value::List {
vals: vec![Value::test_int(0), Value::test_int(5), Value::test_int(7)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(0), Value::test_int(5), Value::test_int(7)],
Span::test_data(),
)),
},
Example {
description: "Returns all matched index, searching from end",
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of -a -e 0x[33 44]",
result: Some(Value::List {
vals: vec![Value::test_int(7), Value::test_int(5), Value::test_int(0)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(7), Value::test_int(5), Value::test_int(0)],
Span::test_data(),
)),
},
Example {
description: "Returns index of pattern for specific column",
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes index-of 0x[11] ColA ColC"#,
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
vals: vec![
Value::test_int(0),
Value::Binary {
val: vec![0x14, 0x15, 0x16],
span: Span::test_data(),
},
Value::binary(vec![0x14, 0x15, 0x16], Span::test_data()),
Value::test_int(-1),
],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
}
fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => index_of_impl(val, args, *val_span),
Value::Binary { val, .. } => index_of_impl(val, args, val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}
@ -157,22 +152,20 @@ fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut iter = input.windows(arg.pattern.len());
if arg.end {
Value::Int {
val: iter
.rev()
Value::int(
iter.rev()
.position(|sub_bytes| sub_bytes == arg.pattern)
.map(|x| (input.len() - arg.pattern.len() - x) as i64)
.unwrap_or(-1),
span,
}
)
} else {
Value::Int {
val: iter
.position(|sub_bytes| sub_bytes == arg.pattern)
Value::int(
iter.position(|sub_bytes| sub_bytes == arg.pattern)
.map(|x| x as i64)
.unwrap_or(-1),
span,
}
)
}
}
}
@ -186,10 +179,7 @@ fn search_all_index(input: &[u8], pattern: &[u8], from_end: bool, span: Span) ->
);
while left >= 0 {
if &input[left as usize..right as usize] == pattern {
result.push(Value::Int {
val: left as i64,
span,
});
result.push(Value::int(left as i64, span));
left -= pattern.len() as isize;
right -= pattern.len() as isize;
} else {
@ -197,7 +187,7 @@ fn search_all_index(input: &[u8], pattern: &[u8], from_end: bool, span: Span) ->
right -= 1;
}
}
Value::List { vals: result, span }
Value::list(result, span)
} else {
// doing find stuff.
let (mut left, mut right) = (0, pattern.len());
@ -205,10 +195,7 @@ fn search_all_index(input: &[u8], pattern: &[u8], from_end: bool, span: Span) ->
let pattern_len = pattern.len();
while right <= input_len {
if &input[left..right] == pattern {
result.push(Value::Int {
val: left as i64,
span,
});
result.push(Value::int(left as i64, span));
left += pattern_len;
right += pattern_len;
} else {
@ -217,7 +204,7 @@ fn search_all_index(input: &[u8], pattern: &[u8], from_end: bool, span: Span) ->
}
}
Value::List { vals: result, span }
Value::list(result, span)
}
}

View File

@ -64,32 +64,30 @@ impl Command for BytesLen {
Example {
description: "Return the lengths of multiple binaries",
example: "[0x[1F FF AA AB] 0x[1F]] | bytes length",
result: Some(Value::List {
vals: vec![Value::test_int(4), Value::test_int(1)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(4), Value::test_int(1)],
Span::test_data(),
)),
},
]
}
}
fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => Value::int(val.len() as i64, *val_span),
Value::Binary { val, .. } => Value::int(val.len() as i64, val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}

View File

@ -87,10 +87,10 @@ impl Command for BytesRemove {
Example {
description: "Remove contents",
example: "0x[10 AA FF AA FF] | bytes remove 0x[10 AA]",
result: Some(Value::Binary {
val: vec![0xFF, 0xAA, 0xFF],
span: Span::test_data(),
}),
result: Some(Value::binary (
vec![0xFF, 0xAA, 0xFF],
Span::test_data(),
)),
},
Example {
description: "Remove all occurrences of find binary in record field",
@ -103,56 +103,54 @@ impl Command for BytesRemove {
Example {
description: "Remove occurrences of find binary from end",
example: "0x[10 AA 10 BB CC AA 10] | bytes remove -e 0x[10]",
result: Some(Value::Binary {
val: vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
span: Span::test_data(),
}),
result: Some(Value::binary (
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
Span::test_data(),
)),
},
Example {
description: "Remove all occurrences of find binary in table",
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes remove 0x[11] ColA ColC",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list (
vec![Value::test_record(Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
vals: vec![
Value::Binary {
val: vec![0x12, 0x13],
span: Span::test_data(),
},
Value::Binary {
val: vec![0x14, 0x15, 0x16],
span: Span::test_data(),
},
Value::Binary {
val: vec![0x17, 0x18, 0x19],
span: Span::test_data(),
},
Value::binary (
vec![0x12, 0x13],
Span::test_data(),
),
Value::binary (
vec![0x14, 0x15, 0x16],
Span::test_data(),
),
Value::binary (
vec![0x17, 0x18, 0x19],
Span::test_data(),
),
]
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
}
fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => remove_impl(val, args, *val_span),
Value::Binary { val, .. } => remove_impl(val, args, val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}
@ -180,7 +178,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut remain = input[..left as usize].iter().copied().rev().collect();
result.append(&mut remain);
result = result.into_iter().rev().collect();
Value::Binary { val: result, span }
Value::binary(result, span)
} else {
let (mut left, mut right) = (0, arg.pattern.len());
while right <= input_len {
@ -200,7 +198,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
// we have something to remove and remove_all is False.
let mut remain = input[left..].to_vec();
result.append(&mut remain);
Value::Binary { val: result, span }
Value::binary(result, span)
}
}

View File

@ -87,64 +87,62 @@ impl Command for BytesReplace {
Example {
description: "Find and replace contents",
example: "0x[10 AA FF AA FF] | bytes replace 0x[10 AA] 0x[FF]",
result: Some(Value::Binary {
val: vec![0xFF, 0xFF, 0xAA, 0xFF],
span: Span::test_data(),
}),
result: Some(Value::binary (
vec![0xFF, 0xFF, 0xAA, 0xFF],
Span::test_data(),
)),
},
Example {
description: "Find and replace all occurrences of find binary",
example: "0x[10 AA 10 BB 10] | bytes replace -a 0x[10] 0x[A0]",
result: Some(Value::Binary {
val: vec![0xA0, 0xAA, 0xA0, 0xBB, 0xA0],
span: Span::test_data(),
}),
result: Some(Value::binary (
vec![0xA0, 0xAA, 0xA0, 0xBB, 0xA0],
Span::test_data(),
)),
},
Example {
description: "Find and replace all occurrences of find binary in table",
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes replace -a 0x[11] 0x[13] ColA ColC",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list (
vec![Value::test_record(Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
vals: vec![
Value::Binary {
val: vec![0x13, 0x12, 0x13],
span: Span::test_data(),
},
Value::Binary {
val: vec![0x14, 0x15, 0x16],
span: Span::test_data(),
},
Value::Binary {
val: vec![0x17, 0x18, 0x19],
span: Span::test_data(),
},
Value::binary (
vec![0x13, 0x12, 0x13],
Span::test_data(),
),
Value::binary (
vec![0x14, 0x15, 0x16],
Span::test_data(),
),
Value::binary (
vec![0x17, 0x18, 0x19],
Span::test_data(),
),
],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
}
fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => replace_impl(val, args, *val_span),
Value::Binary { val, .. } => replace_impl(val, args, val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}
@ -174,10 +172,7 @@ fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut remain = input[left..].to_vec();
replaced.append(&mut remain);
Value::Binary {
val: replaced,
span,
}
Value::binary(replaced, span)
}
#[cfg(test)]

View File

@ -56,47 +56,39 @@ impl Command for BytesReverse {
Example {
description: "Reverse bytes `0x[1F FF AA AA]`",
example: "0x[1F FF AA AA] | bytes reverse",
result: Some(Value::Binary {
val: vec![0xAA, 0xAA, 0xFF, 0x1F],
span: Span::test_data(),
}),
result: Some(Value::binary(
vec![0xAA, 0xAA, 0xFF, 0x1F],
Span::test_data(),
)),
},
Example {
description: "Reverse bytes `0x[FF AA AA]`",
example: "0x[FF AA AA] | bytes reverse",
result: Some(Value::Binary {
val: vec![0xAA, 0xAA, 0xFF],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0xAA, 0xAA, 0xFF], Span::test_data())),
},
]
}
}
fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => {
Value::Binary { val, .. } => {
let mut reversed_input = val.to_vec();
reversed_input.reverse();
Value::Binary {
val: reversed_input,
span: *val_span,
}
Value::binary(reversed_input, val_span)
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}

View File

@ -84,15 +84,15 @@ impl Command for BytesStartsWith {
Ok(v @ Value::Error { .. }) => return Ok(v.clone().into_pipeline_data()),
// Unsupported data
Ok(other) => {
return Ok(Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
return Ok(Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "string and binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
}
)
.into_pipeline_data());
}
Err(err) => return Err(err.to_owned()),
@ -147,22 +147,20 @@ impl Command for BytesStartsWith {
}
fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
let val_span = val.span();
match val {
Value::Binary {
val,
span: val_span,
} => Value::bool(val.starts_with(&args.pattern), *val_span),
Value::Binary { val, .. } => Value::bool(val.starts_with(&args.pattern), val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
}),
},
span,
},
),
}
}

View File

@ -65,18 +65,40 @@ impl HashableValue {
///
/// If the given value is not hashable(mainly because of it is structured data), an error will returned.
pub fn from_value(value: Value, span: Span) -> Result<Self, ShellError> {
let val_span = value.span();
match value {
Value::Bool { val, span } => Ok(HashableValue::Bool { val, span }),
Value::Int { val, span } => Ok(HashableValue::Int { val, span }),
Value::Filesize { val, span } => Ok(HashableValue::Filesize { val, span }),
Value::Duration { val, span } => Ok(HashableValue::Duration { val, span }),
Value::Date { val, span } => Ok(HashableValue::Date { val, span }),
Value::Float { val, span } => Ok(HashableValue::Float {
val: val.to_ne_bytes(),
span,
Value::Bool { val, .. } => Ok(HashableValue::Bool {
val,
span: val_span,
}),
Value::Int { val, .. } => Ok(HashableValue::Int {
val,
span: val_span,
}),
Value::Filesize { val, .. } => Ok(HashableValue::Filesize {
val,
span: val_span,
}),
Value::Duration { val, .. } => Ok(HashableValue::Duration {
val,
span: val_span,
}),
Value::Date { val, .. } => Ok(HashableValue::Date {
val,
span: val_span,
}),
Value::Float { val, .. } => Ok(HashableValue::Float {
val: val.to_ne_bytes(),
span: val_span,
}),
Value::String { val, .. } => Ok(HashableValue::String {
val,
span: val_span,
}),
Value::Binary { val, .. } => Ok(HashableValue::Binary {
val,
span: val_span,
}),
Value::String { val, span } => Ok(HashableValue::String { val, span }),
Value::Binary { val, span } => Ok(HashableValue::Binary { val, span }),
// Explicitly propagate errors instead of dropping them.
Value::Error { error, .. } => Err(*error),
@ -92,17 +114,14 @@ impl HashableValue {
/// Convert from self to nu's core data type `Value`.
pub fn into_value(self) -> Value {
match self {
HashableValue::Bool { val, span } => Value::Bool { val, span },
HashableValue::Int { val, span } => Value::Int { val, span },
HashableValue::Filesize { val, span } => Value::Filesize { val, span },
HashableValue::Duration { val, span } => Value::Duration { val, span },
HashableValue::Date { val, span } => Value::Date { val, span },
HashableValue::Float { val, span } => Value::Float {
val: f64::from_ne_bytes(val),
span,
},
HashableValue::String { val, span } => Value::String { val, span },
HashableValue::Binary { val, span } => Value::Binary { val, span },
HashableValue::Bool { val, span } => Value::bool(val, span),
HashableValue::Int { val, span } => Value::int(val, span),
HashableValue::Filesize { val, span } => Value::filesize(val, span),
HashableValue::Duration { val, span } => Value::duration(val, span),
HashableValue::Date { val, span } => Value::date(val, span),
HashableValue::Float { val, span } => Value::float(f64::from_ne_bytes(val), span),
HashableValue::String { val, span } => Value::string(val, span),
HashableValue::Binary { val, span } => Value::binary(val, span),
}
}
}
@ -167,29 +186,24 @@ mod test {
let span = Span::test_data();
let values = vec![
(
Value::Bool { val: true, span },
Value::bool(true, span),
HashableValue::Bool { val: true, span },
),
(Value::int(1, span), HashableValue::Int { val: 1, span }),
(
Value::Int { val: 1, span },
HashableValue::Int { val: 1, span },
),
(
Value::Filesize { val: 1, span },
Value::filesize(1, span),
HashableValue::Filesize { val: 1, span },
),
(
Value::Duration { val: 1, span },
Value::duration(1, span),
HashableValue::Duration { val: 1, span },
),
(
Value::Date {
val: DateTime::<FixedOffset>::parse_from_rfc2822(
"Wed, 18 Feb 2015 23:16:09 GMT",
)
.unwrap(),
Value::date(
DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT")
.unwrap(),
span,
},
),
HashableValue::Date {
val: DateTime::<FixedOffset>::parse_from_rfc2822(
"Wed, 18 Feb 2015 23:16:09 GMT",
@ -199,17 +213,14 @@ mod test {
},
),
(
Value::String {
val: "1".to_string(),
span,
},
Value::string("1".to_string(), span),
HashableValue::String {
val: "1".to_string(),
span,
},
),
(
Value::Binary { val: vec![1], span },
Value::binary(vec![1], span),
HashableValue::Binary { val: vec![1], span },
),
];
@ -225,22 +236,12 @@ mod test {
fn from_unhashable_value() {
let span = Span::test_data();
let values = [
Value::List {
vals: vec![Value::Bool { val: true, span }],
span,
},
Value::Closure {
val: 0,
captures: HashMap::new(),
span,
},
Value::Nothing { span },
Value::Error {
error: Box::new(ShellError::DidYouMean("what?".to_string(), span)),
span,
},
Value::CellPath {
val: CellPath {
Value::list(vec![Value::bool(true, span)], span),
Value::closure(0, HashMap::new(), span),
Value::nothing(span),
Value::error(ShellError::DidYouMean("what?".to_string(), span), span),
Value::cell_path(
CellPath {
members: vec![PathMember::Int {
val: 0,
span,
@ -248,7 +249,7 @@ mod test {
}],
},
span,
},
),
];
for v in values {
assert!(HashableValue::from_value(v, Span::unknown()).is_err())
@ -259,15 +260,12 @@ mod test {
fn from_to_tobe_same() {
let span = Span::test_data();
let values = vec![
Value::Bool { val: true, span },
Value::Int { val: 1, span },
Value::Filesize { val: 1, span },
Value::Duration { val: 1, span },
Value::String {
val: "1".to_string(),
span,
},
Value::Binary { val: vec![1], span },
Value::bool(true, span),
Value::int(1, span),
Value::filesize(1, span),
Value::duration(1, span),
Value::string("1".to_string(), span),
Value::binary(vec![1], span),
];
for val in values.into_iter() {
let expected_val = val.clone();

View File

@ -51,8 +51,8 @@ impl Command for Histogram {
Example {
description: "Compute a histogram for a list of numbers",
example: "[1 2 1] | histogram",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list (
vec![Value::test_record(Record {
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
vals: vec![
Value::test_int(1),
@ -72,8 +72,8 @@ impl Command for Histogram {
Value::test_string("*********************************"),
],
})],
span: Span::test_data(),
}
Span::test_data(),
)
),
},
Example {
@ -274,16 +274,10 @@ fn histogram_impl(
cols: result_cols.clone(),
vals: vec![
val.into_value(),
Value::Int { val: count, span },
Value::Float {
val: quantile,
span,
},
Value::String {
val: percentage,
span,
},
Value::String { val: freq, span },
Value::int(count, span),
Value::float(quantile, span),
Value::string(percentage, span),
Value::string(freq, span),
],
},
span,
@ -291,11 +285,7 @@ fn histogram_impl(
));
}
result.sort_by(|a, b| b.0.cmp(&a.0));
Value::List {
vals: result.into_iter().map(|x| x.1).collect(),
span,
}
.into_pipeline_data()
Value::list(result.into_iter().map(|x| x.1).collect(), span).into_pipeline_data()
}
#[cfg(test)]

View File

@ -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 {

View File

@ -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
}

View File

@ -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,
},
),
}
}

View File

@ -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())
}
}

View File

@ -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)
}

View File

@ -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,
),
}
}

View File

@ -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,
},
),
}
}

View File

@ -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> {

View File

@ -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,
),
}
}

View File

@ -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())
}

View File

@ -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,
},
),
}
}

View File

@ -109,8 +109,9 @@ fn action(
"main".to_string()
};
let val_span = input.span();
match input {
Value::List { vals, span } => {
Value::List { vals, .. } => {
// find the column names, and sqlite data types
let columns = get_columns_with_sqlite_types(vals);
@ -135,11 +136,10 @@ fn action(
.join(",")
}
// Number formats so keep them without quotes
Value::Int { val: _, span: _ }
| Value::Float { val: _, span: _ }
| Value::Filesize { val: _, span: _ }
| Value::Duration { val: _, span: _ } =>
nu_value_to_string(list_value.clone(), ""),
Value::Int { .. }
| Value::Float { .. }
| Value::Filesize { .. }
| Value::Duration { .. } => nu_value_to_string(list_value.clone(), ""),
_ =>
// String formats so add quotes around them
format!("'{}'", nu_value_to_string(list_value.clone(), "")),
@ -210,7 +210,7 @@ fn action(
})?;
// and we're done
Ok(Value::Nothing { span: *span })
Ok(Value::nothing(val_span))
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => Err(*error.clone()),

View File

@ -56,8 +56,9 @@ impl SQLiteDatabase {
}
pub fn try_from_value(value: Value) -> Result<Self, ShellError> {
let span = value.span();
match value {
Value::CustomValue { val, span } => match val.as_any().downcast_ref::<Self>() {
Value::CustomValue { val, .. } => match val.as_any().downcast_ref::<Self>() {
Some(db) => Ok(Self {
path: db.path.clone(),
ctrlc: db.ctrlc.clone(),
@ -84,10 +85,7 @@ impl SQLiteDatabase {
}
pub fn into_value(self, span: Span) -> Value {
Value::CustomValue {
val: Box::new(self),
span,
}
Value::custom_value(Box::new(self), span)
}
pub fn query(&self, sql: &Spanned<String>, call_span: Span) -> Result<Value, ShellError> {
@ -280,10 +278,7 @@ impl CustomValue for SQLiteDatabase {
ctrlc: self.ctrlc.clone(),
};
Value::CustomValue {
val: Box::new(cloned),
span,
}
Value::custom_value(Box::new(cloned), span)
}
fn value_string(&self) -> String {
@ -393,10 +388,7 @@ fn prepared_statement_to_nu_list(
for row_result in row_results {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
// return whatever we have so far, let the caller decide whether to use it
return Ok(Value::List {
vals: row_values,
span: call_span,
});
return Ok(Value::list(row_values, call_span));
}
if let Ok(row_value) = row_result {
@ -404,10 +396,7 @@ fn prepared_statement_to_nu_list(
}
}
Ok(Value::List {
vals: row_values,
span: call_span,
})
Ok(Value::list(row_values, call_span))
}
fn read_entire_sqlite_db(
@ -450,28 +439,17 @@ pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec<S
pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {
match value {
ValueRef::Null => Value::Nothing { span },
ValueRef::Integer(i) => Value::Int { val: i, span },
ValueRef::Real(f) => Value::Float { val: f, span },
ValueRef::Null => Value::nothing(span),
ValueRef::Integer(i) => Value::int(i, span),
ValueRef::Real(f) => Value::float(f, span),
ValueRef::Text(buf) => {
let s = match std::str::from_utf8(buf) {
Ok(v) => v,
Err(_) => {
return Value::Error {
error: Box::new(ShellError::NonUtf8(span)),
span,
}
}
Err(_) => return Value::error(ShellError::NonUtf8(span), span),
};
Value::String {
val: s.to_string(),
span,
}
Value::string(s.to_string(), span)
}
ValueRef::Blob(u) => Value::Binary {
val: u.to_vec(),
span,
},
ValueRef::Blob(u) => Value::binary(u.to_vec(), span),
}
}
@ -506,10 +484,7 @@ mod test {
let expected = Value::test_record(Record {
cols: vec!["person".to_string()],
vals: vec![Value::List {
vals: vec![],
span: Span::test_data(),
}],
vals: vec![Value::list(vec![], Span::test_data())],
});
assert_eq!(converted_db, expected);
@ -539,25 +514,22 @@ mod test {
let expected = Value::test_record(Record {
cols: vec!["item".to_string()],
vals: vec![Value::List {
vals: vec![
vals: vec![Value::list(
vec![
Value::test_record(Record {
cols: vec!["id".to_string(), "name".to_string()],
vals: vec![Value::Int { val: 123, span }, Value::Nothing { span }],
vals: vec![Value::int(123, span), Value::nothing(span)],
}),
Value::test_record(Record {
cols: vec!["id".to_string(), "name".to_string()],
vals: vec![
Value::Int { val: 456, span },
Value::String {
val: "foo bar".to_string(),
span,
},
Value::int(456, span),
Value::string("foo bar".to_string(), span),
],
}),
],
span,
}],
)],
});
assert_eq!(converted_db, expected);

View File

@ -58,15 +58,15 @@ fn date(
) -> Result<PipelineData, ShellError> {
let head = call.head;
Ok(Value::String {
val: get_full_help(
Ok(Value::string(
get_full_help(
&Date.signature(),
&Date.examples(),
engine_state,
stack,
false,
),
span: head,
}
head,
)
.into_pipeline_data())
}

View File

@ -64,35 +64,24 @@ impl Command for SubCommand {
}
fn helper(value: Value, head: Span) -> Value {
let span = value.span();
match value {
Value::Nothing { span: _ } => {
Value::Nothing { .. } => {
let dt = Local::now();
Value::String {
val: humanize_date(dt.with_timezone(dt.offset())),
span: head,
}
Value::string(humanize_date(dt.with_timezone(dt.offset())), head)
}
Value::String {
val,
span: val_span,
} => {
let dt = parse_date_from_string(&val, val_span);
Value::String { val, .. } => {
let dt = parse_date_from_string(&val, span);
match dt {
Ok(x) => Value::String {
val: humanize_date(x),
span: head,
},
Ok(x) => Value::string(humanize_date(x), head),
Err(e) => e,
}
}
Value::Date { val, span: _ } => Value::String {
val: humanize_date(val),
span: head,
},
_ => Value::Error {
error: Box::new(ShellError::DatetimeParseError(value.debug_value(), head)),
span: head,
},
Value::Date { val, .. } => Value::string(humanize_date(val), head),
_ => Value::error(
ShellError::DatetimeParseError(value.debug_value(), head),
head,
),
}
}

View File

@ -52,13 +52,13 @@ impl Command for SubCommand {
vec![Example {
example: "date list-timezone | where timezone =~ Shanghai",
description: "Show timezone(s) that contains 'Shanghai'",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["timezone".into()],
vals: vec![Value::test_string("Asia/Shanghai")],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}
}

View File

@ -35,11 +35,7 @@ impl Command for SubCommand {
) -> Result<PipelineData, ShellError> {
let head = call.head;
let dt = Local::now();
Ok(Value::Date {
val: dt.with_timezone(dt.offset()),
span: head,
}
.into_pipeline_data())
Ok(Value::date(dt.with_timezone(dt.offset()), head).into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {

View File

@ -62,20 +62,14 @@ impl Command for SubCommand {
"timezone".into(),
];
let 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::Int {
val: 123_000_000,
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::int(123_000_000, span),
Value::string("+02:00", span),
];
Some(Value::test_record(Record { cols, vals }))
};
@ -93,17 +87,14 @@ impl Command for SubCommand {
"timezone".into(),
];
let 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::Int { val: 0, 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::int(0, span),
Value::string("+02:00", span),
];
Some(Value::test_record(Record { cols, vals }))
};
@ -150,24 +141,19 @@ fn parse_date_into_table(date: DateTime<FixedOffset>, head: Span) -> Value {
}
fn helper(val: Value, head: Span) -> Value {
let span = val.span();
match val {
Value::String {
val,
span: val_span,
} => match parse_date_from_string(&val, val_span) {
Value::String { val, .. } => match parse_date_from_string(&val, span) {
Ok(date) => parse_date_into_table(date, head),
Err(e) => e,
},
Value::Nothing { span: _ } => {
Value::Nothing { .. } => {
let now = Local::now();
let n = now.with_timezone(now.offset());
parse_date_into_table(n, head)
}
Value::Date { val, span: _ } => parse_date_into_table(val, head),
_ => Value::Error {
error: Box::new(DatetimeParseError(val.debug_value(), head)),
span: head,
},
Value::Date { val, .. } => parse_date_into_table(val, head),
_ => Value::error(DatetimeParseError(val.debug_value(), head), head),
}
}

View File

@ -62,22 +62,19 @@ impl Command for SubCommand {
"timezone".into(),
];
let 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::Int { val: 789, 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::int(789, span),
Value::string("+02:00".to_string(), span),
];
Some(Value::List {
vals: vec![Value::test_record(Record { cols, vals })],
Some(Value::list(
vec![Value::test_record(Record { cols, vals })],
span,
})
))
};
let example_result_2 = || {
@ -93,22 +90,19 @@ impl Command for SubCommand {
"timezone".into(),
];
let 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::Int { val: 0, 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::int(0, span),
Value::string("+02:00".to_string(), span),
];
Some(Value::List {
vals: vec![Value::test_record(Record { cols, vals })],
Some(Value::list(
vec![Value::test_record(Record { cols, vals })],
span,
})
))
};
vec![
@ -152,24 +146,19 @@ fn parse_date_into_table(date: DateTime<FixedOffset>, head: Span) -> Value {
}
fn helper(val: Value, head: Span) -> Value {
let val_span = val.span();
match val {
Value::String {
val,
span: val_span,
} => match parse_date_from_string(&val, val_span) {
Value::String { val, .. } => match parse_date_from_string(&val, val_span) {
Ok(date) => parse_date_into_table(date, head),
Err(e) => e,
},
Value::Nothing { span: _ } => {
Value::Nothing { .. } => {
let now = Local::now();
let n = now.with_timezone(now.offset());
parse_date_into_table(n, head)
}
Value::Date { val, span: _ } => parse_date_into_table(val, head),
_ => Value::Error {
error: Box::new(DatetimeParseError(val.debug_value(), head)),
span: head,
},
Value::Date { val, .. } => parse_date_into_table(val, head),
_ => Value::error(DatetimeParseError(val.debug_value(), head), head),
}
}

View File

@ -71,10 +71,7 @@ impl Command for SubCommand {
.expect("to timezone: help example is invalid")
.with_ymd_and_hms(2020, 10, 10, 13, 00, 00)
{
LocalResult::Single(dt) => Some(Value::Date {
val: dt,
span: Span::test_data(),
}),
LocalResult::Single(dt) => Some(Value::date(dt, Span::test_data())),
_ => panic!("to timezone: help example is invalid"),
};
@ -109,12 +106,10 @@ impl Command for SubCommand {
}
fn helper(value: Value, head: Span, timezone: &Spanned<String>) -> Value {
let val_span = value.span();
match value {
Value::Date { val, span: _ } => _to_timezone(val, timezone, head),
Value::String {
val,
span: val_span,
} => {
Value::Date { val, .. } => _to_timezone(val, timezone, head),
Value::String { val, .. } => {
let time = parse_date_from_string(&val, val_span);
match time {
Ok(dt) => _to_timezone(dt, timezone, head),
@ -122,27 +117,27 @@ fn helper(value: Value, head: Span, timezone: &Spanned<String>) -> Value {
}
}
Value::Nothing { span: _ } => {
Value::Nothing { .. } => {
let dt = Local::now();
_to_timezone(dt.with_timezone(dt.offset()), timezone, head)
}
_ => Value::Error {
error: Box::new(ShellError::DatetimeParseError(value.debug_value(), head)),
span: head,
},
_ => Value::error(
ShellError::DatetimeParseError(value.debug_value(), head),
head,
),
}
}
fn _to_timezone(dt: DateTime<FixedOffset>, timezone: &Spanned<String>, span: Span) -> Value {
match datetime_in_timezone(&dt, timezone.item.as_str()) {
Ok(dt) => Value::Date { val: dt, span },
Err(_) => Value::Error {
error: Box::new(ShellError::TypeMismatch {
Ok(dt) => Value::date(dt, span),
Err(_) => Value::error(
ShellError::TypeMismatch {
err_message: String::from("invalid time zone"),
span: timezone.span,
}),
span: timezone.span,
},
},
timezone.span,
),
}
}

View File

@ -14,16 +14,16 @@ pub(crate) fn parse_date_from_string(
match offset.from_local_datetime(&native_dt) {
LocalResult::Single(d) => Ok(d),
LocalResult::Ambiguous(d, _) => Ok(d),
LocalResult::None => Err(Value::Error {
error: Box::new(ShellError::DatetimeParseError(input.to_string(), span)),
LocalResult::None => Err(Value::error(
ShellError::DatetimeParseError(input.to_string(), span),
span,
}),
)),
}
}
Err(_) => Err(Value::Error {
error: Box::new(ShellError::DatetimeParseError(input.to_string(), span)),
Err(_) => Err(Value::error(
ShellError::DatetimeParseError(input.to_string(), span),
span,
}),
)),
}
}
@ -289,8 +289,5 @@ pub(crate) fn generate_strftime_list(head: Span, show_parse_only_formats: bool)
));
}
Value::List {
vals: records,
span: head,
}
Value::list(records, head)
}

View File

@ -97,22 +97,22 @@ impl Command for Ast {
);
Ok(output_record.into_pipeline_data())
} else {
let block_value = Value::String {
val: if minify {
let block_value = Value::string(
if minify {
format!("{block_output:?}")
} else {
format!("{block_output:#?}")
},
span: pipeline.span,
};
let error_value = Value::String {
val: if minify {
pipeline.span,
);
let error_value = Value::string(
if minify {
format!("{error_output:?}")
} else {
format!("{error_output:#?}")
},
span: pipeline.span,
};
pipeline.span,
);
let output_record = Value::record(
record! {
"block" => block_value,

View File

@ -44,15 +44,9 @@ impl Command for Debug {
input.map(
move |x| {
if raw {
Value::String {
val: x.debug_value(),
span: head,
}
Value::string(x.debug_value(), head)
} else {
Value::String {
val: x.debug_string(", ", &config),
span: head,
}
Value::string(x.debug_string(", ", &config), head)
}
},
engine_state.ctrlc.clone(),
@ -69,23 +63,23 @@ impl Command for Debug {
Example {
description: "Debug print a list",
example: "['hello'] | debug",
result: Some(Value::List {
vals: vec![Value::test_string("hello")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("hello")],
Span::test_data(),
)),
},
Example {
description: "Debug print a table",
example:
"[[version patch]; ['0.1.0' false] ['0.1.1' true] ['0.2.0' false]] | debug",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("{version: 0.1.0, patch: false}"),
Value::test_string("{version: 0.1.1, patch: true}"),
Value::test_string("{version: 0.2.0, patch: false}"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -213,10 +213,7 @@ fn get_expression_as_value(
) -> Value {
match eval_expression(engine_state, stack, inner_expr) {
Ok(v) => v,
Err(error) => Value::Error {
error: Box::new(error),
span: inner_expr.span,
},
Err(error) => Value::error(error, inner_expr.span),
}
}

View File

@ -198,6 +198,7 @@ mod util {
/// Try to build column names and a table grid.
pub fn collect_input(value: Value) -> (Vec<String>, Vec<Vec<String>>) {
let span = value.span();
match value {
Value::Record { val: record, .. } => (
record.cols,
@ -217,13 +218,10 @@ mod util {
(columns, data)
}
Value::String { val, span } => {
Value::String { val, .. } => {
let lines = val
.lines()
.map(|line| Value::String {
val: line.to_string(),
span,
})
.map(|line| Value::string(line.to_string(), span))
.map(|val| vec![debug_string_without_formatting(&val)])
.collect();

View File

@ -79,10 +79,7 @@ impl Command for TimeIt {
let end_time = Instant::now();
let output = Value::Duration {
val: (end_time - start_time).as_nanos() as i64,
span: call.head,
};
let output = Value::duration((end_time - start_time).as_nanos() as i64, call.head);
Ok(output.into_pipeline_data())
}

View File

@ -34,16 +34,16 @@ impl Command for View {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::String {
val: get_full_help(
Ok(Value::string(
get_full_help(
&View.signature(),
&View.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head,
}
call.head,
)
.into_pipeline_data())
}
}

View File

@ -47,11 +47,7 @@ impl Command for ViewFiles {
));
}
Ok(Value::List {
vals: records,
span: call.head,
}
.into_pipeline_data())
Ok(Value::list(records, call.head).into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {

View File

@ -34,16 +34,16 @@ impl Command for ConfigMeta {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::String {
val: get_full_help(
Ok(Value::string(
get_full_help(
&ConfigMeta.signature(),
&ConfigMeta.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head,
}
call.head,
)
.into_pipeline_data())
}

View File

@ -58,9 +58,7 @@ impl Command for ExportEnv {
Example {
description: "Set an environment variable",
example: r#"export-env { $env.SPAM = 'eggs' }"#,
result: Some(Value::Nothing {
span: Span::test_data(),
}),
result: Some(Value::nothing(Span::test_data())),
},
Example {
description: "Set an environment variable and examine its value",

View File

@ -159,10 +159,7 @@ impl Command for Cd {
}
};
let path_value = Value::String {
val: path.clone(),
span,
};
let path_value = Value::string(path.clone(), span);
if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") {
stack.add_env_var("OLDPWD".into(), oldpwd)

View File

@ -394,19 +394,13 @@ fn interactive_copy(
format!("cp: overwrite '{}'? ", dst.to_string_lossy()),
);
if let Err(e) = interaction {
Value::Error {
error: Box::new(ShellError::GenericError(
e.to_string(),
e.to_string(),
Some(span),
None,
Vec::new(),
)),
Value::error(
ShellError::GenericError(e.to_string(), e.to_string(), Some(span), None, Vec::new()),
span,
}
)
} else if !confirmed {
let msg = format!("{:} not copied to {:}", src.display(), dst.display());
Value::String { val: msg, span }
Value::string(msg, span)
} else {
copy_impl(src, dst, span, &None)
}
@ -425,7 +419,7 @@ fn copy_file(
match std::fs::copy(&src, &dst) {
Ok(_) => {
let msg = format!("copied {:} to {:}", src.display(), dst.display());
Value::String { val: msg, span }
Value::string(msg, span)
}
Err(e) => convert_io_error(e, src, dst, span),
}
@ -521,7 +515,7 @@ fn copy_file_with_progressbar(
let msg = format!("copied {:} to {:}", src.display(), dst.display());
bar.finished_msg(format!(" {} copied!", &file_name));
Value::String { val: msg, span }
Value::string(msg, span)
}
fn copy_symlink(
@ -534,16 +528,16 @@ fn copy_symlink(
let target_path = match target_path {
Ok(p) => p,
Err(err) => {
return Value::Error {
error: Box::new(ShellError::GenericError(
return Value::error(
ShellError::GenericError(
err.to_string(),
err.to_string(),
Some(span),
None,
vec![],
)),
),
span,
}
)
}
};
@ -566,18 +560,12 @@ fn copy_symlink(
match create_symlink(target_path.as_path(), dst.as_path()) {
Ok(_) => {
let msg = format!("copied {:} to {:}", src.display(), dst.display());
Value::String { val: msg, span }
Value::string(msg, span)
}
Err(e) => Value::Error {
error: Box::new(ShellError::GenericError(
e.to_string(),
e.to_string(),
Some(span),
None,
vec![],
)),
Err(e) => Value::error(
ShellError::GenericError(e.to_string(), e.to_string(), Some(span), None, vec![]),
span,
},
),
}
}
@ -618,8 +606,5 @@ fn convert_io_error(error: std::io::Error, src: PathBuf, dst: PathBuf, span: Spa
_ => ShellError::IOErrorSpanned(message_src, span),
};
Value::Error {
error: Box::new(shell_error),
span,
}
Value::error(shell_error, span)
}

View File

@ -142,16 +142,19 @@ impl Command for Glob {
let no_files = call.has_flag("no-file");
let no_symlinks = call.has_flag("no-symlink");
let (not_patterns, not_pattern_span): (Vec<String>, Span) = if let Some(Value::List {
vals: pats,
span: pat_span,
}) =
call.get_flag(engine_state, stack, "not")?
{
let p = convert_patterns(pats.as_slice())?;
(p, pat_span)
} else {
(vec![], span)
let not_flag: Option<Value> = call.get_flag(engine_state, stack, "not")?;
let (not_patterns, not_pattern_span): (Vec<String>, Span) = match not_flag {
None => (vec![], span),
Some(f) => {
let pat_span = f.span();
match f {
Value::List { vals: pats, .. } => {
let p = convert_patterns(pats.as_slice())?;
(p, pat_span)
}
_ => (vec![], span),
}
}
};
if glob_pattern.item.is_empty() {
@ -261,10 +264,10 @@ fn glob_to_value<'a>(
|| no_files && file_type.is_file()
|| no_symlinks && file_type.is_symlink())
{
result.push(Value::String {
val: entry.into_path().to_string_lossy().to_string(),
result.push(Value::string(
entry.into_path().to_string_lossy().to_string(),
span,
});
));
}
}

View File

@ -255,19 +255,13 @@ impl Command for Ls {
);
match entry {
Ok(value) => Some(value),
Err(err) => Some(Value::Error {
error: Box::new(err),
span: call_span,
}),
Err(err) => Some(Value::error(err, call_span)),
}
}
Err(err) => Some(Value::Error {
error: Box::new(err),
span: call_span,
}),
Err(err) => Some(Value::error(err, call_span)),
}
}
_ => Some(Value::Nothing { span: call_span }),
_ => Some(Value::nothing(call_span)),
})
.into_pipeline_data_with_metadata(
Box::new(PipelineMetadata {

View File

@ -79,7 +79,7 @@ impl Command for Mkdir {
if is_verbose {
let val = format!("{:}", dir.to_string_lossy());
stream.push_back(Value::String { val, span });
stream.push_back(Value::string(val, span));
}
}

View File

@ -200,10 +200,7 @@ impl Command for Mv {
update_mode,
);
if let Err(error) = result {
Some(Value::Error {
error: Box::new(error),
span: spanned_source.span,
})
Some(Value::error(error, spanned_source.span))
} else if verbose {
let val = match result {
Ok(true) => format!(
@ -217,7 +214,7 @@ impl Command for Mv {
destination.to_string_lossy()
),
};
Some(Value::String { val, span })
Some(Value::string(val, span))
} else {
None
}

View File

@ -428,10 +428,7 @@ fn rm(
if let Err(e) = result {
let msg = format!("Could not delete {:}: {e:}", f.to_string_lossy());
Value::Error {
error: Box::new(ShellError::RemoveNotPossible(msg, span)),
span,
}
Value::error(ShellError::RemoveNotPossible(msg, span), span)
} else if verbose {
let msg = if interactive && !confirmed {
"not deleted"
@ -439,35 +436,35 @@ fn rm(
"deleted"
};
let val = format!("{} {:}", msg, f.to_string_lossy());
Value::String { val, span }
Value::string(val, span)
} else {
Value::Nothing { span }
Value::nothing(span)
}
} else {
let msg = format!("Cannot remove {:}. try --recursive", f.to_string_lossy());
Value::Error {
error: Box::new(ShellError::GenericError(
Value::error(
ShellError::GenericError(
msg,
"cannot remove non-empty directory".into(),
Some(span),
None,
Vec::new(),
)),
),
span,
}
)
}
} else {
let msg = format!("no such file or directory: {:}", f.to_string_lossy());
Value::Error {
error: Box::new(ShellError::GenericError(
Value::error(
ShellError::GenericError(
msg,
"no such file or directory".into(),
Some(span),
None,
Vec::new(),
)),
),
span,
}
)
}
})
.filter(|x| !matches!(x.get_type(), Type::Nothing))

View File

@ -213,7 +213,7 @@ impl Command for Watch {
engine_state,
stack,
&block,
Value::Nothing { span: call.span() }.into_pipeline_data(),
Value::nothing(call.span()).into_pipeline_data(),
call.redirect_stdout,
call.redirect_stderr,
);

View File

@ -42,57 +42,57 @@ only unwrap the outer list, and leave the variable's contents untouched."#
Example {
example: "[0,1,2,3] | append 4",
description: "Append one integer to a list",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "0 | append [1 2 3]",
description: "Append a list to an item",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: r#""a" | append ["b"] "#,
description: "Append a list of string to a string",
result: Some(Value::List {
vals: vec![Value::test_string("a"), Value::test_string("b")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("a"), Value::test_string("b")],
Span::test_data(),
)),
},
Example {
example: "[0,1] | append [2,3,4]",
description: "Append three integer items",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "[0,1] | append [2,nu,4,shell]",
description: "Append integers and strings",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
@ -100,8 +100,8 @@ only unwrap the outer list, and leave the variable's contents untouched."#
Value::test_int(4),
Value::test_string("shell"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -128,8 +128,7 @@ only unwrap the outer list, and leave the variable's contents untouched."#
fn process_value(val: Value) -> Vec<Value> {
match val {
Value::List {
vals: input_vals,
span: _,
vals: input_vals, ..
} => {
let mut output = vec![];
for input_val in input_vals {

View File

@ -36,22 +36,22 @@ impl Command for Columns {
Example {
example: "{ acronym:PWD, meaning:'Print Working Directory' } | columns",
description: "Get the columns from the record",
result: Some(Value::List {
vals: vec![Value::test_string("acronym"), Value::test_string("meaning")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("acronym"), Value::test_string("meaning")],
Span::test_data(),
)),
},
Example {
example: "[[name,age,grade]; [bill,20,a]] | columns",
description: "Get the columns from the table",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("name"),
Value::test_string("age"),
Value::test_string("grade"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "[[name,age,grade]; [bill,20,a]] | columns | first",
@ -87,30 +87,57 @@ fn getcol(
let metadata = input.metadata();
match input {
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(
Value::List {
vals: input_vals,
span,
},
..,
) => {
let input_cols = get_columns(&input_vals);
Ok(input_cols
.into_iter()
.map(move |x| Value::String { val: x, span })
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
PipelineData::Value(Value::CustomValue { val, span }, ..) => {
// TODO: should we get CustomValue to expose columns in a more efficient way?
// Would be nice to be able to get columns without generating the whole value
let input_as_base_value = val.to_base_value(span)?;
let input_cols = get_columns(&[input_as_base_value]);
Ok(input_cols
.into_iter()
.map(move |x| Value::String { val: x, span })
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
PipelineData::Value(v, ..) => {
let span = v.span();
match v {
Value::List {
vals: input_vals, ..
} => {
let input_cols = get_columns(&input_vals);
Ok(input_cols
.into_iter()
.map(move |x| Value::string(x, span))
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
Value::CustomValue { val, .. } => {
// TODO: should we get CustomValue to expose columns in a more efficient way?
// Would be nice to be able to get columns without generating the whole value
let input_as_base_value = val.to_base_value(span)?;
let input_cols = get_columns(&[input_as_base_value]);
Ok(input_cols
.into_iter()
.map(move |x| Value::string(x, span))
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
Value::LazyRecord { val, .. } => {
Ok({
// Unfortunate casualty to LazyRecord's column_names not generating 'static strs
let cols: Vec<_> =
val.column_names().iter().map(|s| s.to_string()).collect();
cols.into_iter()
.map(move |x| Value::string(x, head))
.into_pipeline_data(ctrlc)
.set_metadata(metadata)
})
}
Value::Record { val, .. } => Ok(val
.cols
.into_iter()
.map(move |x| Value::string(x, head))
.into_pipeline_data(ctrlc)
.set_metadata(metadata)),
// Propagate errors
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record or table".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
}
}
PipelineData::ListStream(stream, ..) => {
let v: Vec<_> = stream.into_iter().collect();
@ -118,33 +145,10 @@ fn getcol(
Ok(input_cols
.into_iter()
.map(move |x| Value::String { val: x, span: head })
.map(move |x| Value::string(x, head))
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
PipelineData::Value(Value::LazyRecord { val, .. }, ..) => Ok({
// Unfortunate casualty to LazyRecord's column_names not generating 'static strs
let cols: Vec<_> = val.column_names().iter().map(|s| s.to_string()).collect();
cols.into_iter()
.map(move |x| Value::String { val: x, span: head })
.into_pipeline_data(ctrlc)
.set_metadata(metadata)
}),
PipelineData::Value(Value::Record { val, .. }, ..) => Ok(val
.cols
.into_iter()
.map(move |x| Value::String { val: x, span: head })
.into_pipeline_data(ctrlc)
.set_metadata(metadata)),
// Propagate errors
PipelineData::Value(Value::Error { error, .. }, ..) => Err(*error),
PipelineData::Value(other, ..) => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record or table".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
PipelineData::ExternalStream { .. } => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record or table".into(),
wrong_type: "raw data".into(),

View File

@ -54,29 +54,26 @@ impl Command for Compact {
Example {
description: "Filter out all records where 'Hello' is null (returns nothing)",
example: r#"[["Hello" "World"]; [null 3]] | compact Hello"#,
result: Some(Value::List {
vals: vec![],
span: Span::test_data(),
}),
result: Some(Value::list(vec![], Span::test_data())),
},
Example {
description: "Filter out all records where 'World' is null (Returns the table)",
example: r#"[["Hello" "World"]; [null 3]] | compact World"#,
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["Hello".into(), "World".into()],
vals: vec![Value::nothing(Span::test_data()), Value::test_int(3)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Filter out all instances of nothing from a list (Returns [1,2])",
example: r#"[1, null, 2] | compact"#,
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(2)],
Span::test_data(),
)),
},
]
}

View File

@ -57,15 +57,15 @@ impl Command for Default {
Example {
description: "Replace the `null` value in a list",
example: "[1, 2, null, 4] | default 3",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -84,31 +84,33 @@ fn default(
if let Some(column) = column {
input.map(
move |item| match item {
Value::Record {
val: mut record,
span,
} => {
let mut idx = 0;
let mut found = false;
move |item| {
let span = item.span();
match item {
Value::Record {
val: mut record, ..
} => {
let mut idx = 0;
let mut found = false;
while idx < record.len() {
if record.cols[idx] == column.item {
found = true;
if matches!(record.vals[idx], Value::Nothing { .. }) {
record.vals[idx] = value.clone();
while idx < record.len() {
if record.cols[idx] == column.item {
found = true;
if matches!(record.vals[idx], Value::Nothing { .. }) {
record.vals[idx] = value.clone();
}
}
idx += 1;
}
idx += 1;
}
if !found {
record.push(column.item.clone(), value.clone());
}
if !found {
record.push(column.item.clone(), value.clone());
}
Value::record(record, span)
Value::record(record, span)
}
_ => item,
}
_ => item,
},
ctrlc,
)

View File

@ -64,8 +64,8 @@ impl Command for DropColumn {
vec![Example {
description: "Remove the last column of a table",
example: "[[lib, extension]; [nu-lib, rs] [nu-core, rb]] | drop column",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["lib".into()],
vals: vec![Value::test_string("nu-lib")],
@ -75,8 +75,8 @@ impl Command for DropColumn {
vals: vec![Value::test_string("nu-core")],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}
}
@ -90,32 +90,6 @@ fn dropcol(
let mut keep_columns = vec![];
match input {
PipelineData::Value(
Value::List {
vals: input_vals,
span,
},
..,
) => {
let mut output = vec![];
let input_cols = get_input_cols(input_vals.clone());
let kc = get_keep_columns(input_cols, columns);
keep_columns = get_cellpath_columns(kc, span);
for input_val in input_vals {
let mut record = Record::new();
for path in &keep_columns {
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
record.push(path.into_string(), fetcher);
}
output.push(Value::record(record, span))
}
Ok(output
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
}
PipelineData::ListStream(stream, ..) => {
let mut output = vec![];
@ -139,14 +113,39 @@ fn dropcol(
.into_pipeline_data(engine_state.ctrlc.clone()))
}
PipelineData::Value(v, ..) => {
let mut record = Record::new();
let val_span = v.span();
if let Value::List {
vals: input_vals, ..
} = v
{
let mut output = vec![];
let input_cols = get_input_cols(input_vals.clone());
let kc = get_keep_columns(input_cols, columns);
keep_columns = get_cellpath_columns(kc, val_span);
for cell_path in &keep_columns {
let result = v.clone().follow_cell_path(&cell_path.members, false)?;
record.push(cell_path.into_string(), result);
for input_val in input_vals {
let mut record = Record::new();
for path in &keep_columns {
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
record.push(path.into_string(), fetcher);
}
output.push(Value::record(record, val_span))
}
Ok(output
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut record = Record::new();
for cell_path in &keep_columns {
let result = v.clone().follow_cell_path(&cell_path.members, false)?;
record.push(cell_path.into_string(), result);
}
Ok(Value::record(record, span).into_pipeline_data())
}
Ok(Value::record(record, span).into_pipeline_data())
}
x => Ok(x),
}
@ -163,10 +162,7 @@ fn get_input_cols(input: Vec<Value>) -> Vec<String> {
fn get_cellpath_columns(keep_cols: Vec<String>, span: Span) -> Vec<CellPath> {
let mut output = vec![];
for keep_col in keep_cols {
let val = Value::String {
val: keep_col,
span,
};
let val = Value::string(keep_col, span);
let cell_path = match CellPath::from_value(&val) {
Ok(v) => v,
Err(_) => return vec![],

View File

@ -41,42 +41,42 @@ impl Command for Drop {
Example {
example: "[0,1,2,3] | drop",
description: "Remove the last item of a list",
result: Some(Value::List {
vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3] | drop 0",
description: "Remove zero item of a list",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3] | drop 2",
description: "Remove the last two items of a list",
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: "Remove the last row in a table",
example: "[[a, b]; [1, 2] [3, 4]] | drop 1",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["a".to_string(), "b".to_string()],
vals: vec![Value::test_int(1), Value::test_int(2)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -44,58 +44,55 @@ impl Command for DropNth {
Example {
example: "[sam,sarah,2,3,4,5] | drop nth 0 1 2",
description: "Drop the first, second, and third row",
result: Some(Value::List {
vals: vec![Value::test_int(3), Value::test_int(4), Value::test_int(5)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(3), Value::test_int(4), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | drop nth 0 1 2",
description: "Drop the first, second, and third row",
result: Some(Value::List {
vals: vec![Value::test_int(3), Value::test_int(4), Value::test_int(5)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(3), Value::test_int(4), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | drop nth 0 2 4",
description: "Drop rows 0 2 4",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | drop nth 2 0 4",
description: "Drop rows 2 0 4",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
description: "Drop range rows from second to fourth",
example: "[first second third fourth fifth] | drop nth (1..3)",
result: Some(Value::List {
vals: vec![Value::test_string("first"), Value::test_string("fifth")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("first"), Value::test_string("fifth")],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | drop nth 1..",
description: "Drop all rows except first row",
result: Some(Value::List {
vals: vec![Value::test_int(0)],
span: Span::test_data(),
}),
result: Some(Value::list(vec![Value::test_int(0)], Span::test_data())),
},
Example {
example: "[0,1,2,3,4,5] | drop nth 3..",
description: "Drop rows 3,4,5",
result: Some(Value::List {
vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
Span::test_data(),
)),
},
]
}
@ -145,7 +142,7 @@ impl Command for DropNth {
let mut to = to as usize;
let from = from as usize;
if let PipelineData::Value(Value::List { ref vals, span: _ }, _) = input {
if let PipelineData::Value(Value::List { ref vals, .. }, _) = input {
let max = from + vals.len() - 1;
if to > max {
to = max;

View File

@ -57,60 +57,50 @@ with 'transpose' first."#
let stream_test_1 = vec![Value::test_int(2), Value::test_int(4), Value::test_int(6)];
let stream_test_2 = vec![
Value::Nothing {
span: Span::test_data(),
},
Value::nothing(Span::test_data()),
Value::test_string("found 2!"),
Value::Nothing {
span: Span::test_data(),
},
Value::nothing(Span::test_data()),
];
vec![
Example {
example: "[1 2 3] | each {|e| 2 * $e }",
description: "Multiplies elements in the list",
result: Some(Value::List {
vals: stream_test_1,
span: Span::test_data(),
}),
result: Some(Value::list(stream_test_1, Span::test_data())),
},
Example {
example: "{major:2, minor:1, patch:4} | values | each {|| into string }",
description: "Produce a list of values in the record, converted to string",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("2"),
Value::test_string("1"),
Value::test_string("4"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: r#"[1 2 3 2] | each {|e| if $e == 2 { "two" } }"#,
description: "Produce a list that has \"two\" for each 2 in the input",
result: Some(Value::List {
vals: vec![Value::test_string("two"), Value::test_string("two")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("two"), Value::test_string("two")],
Span::test_data(),
)),
},
Example {
example: r#"[1 2 3] | enumerate | each {|e| if $e.item == 2 { $"found 2 at ($e.index)!"} }"#,
description:
"Iterate over each element, producing a list showing indexes of any 2s",
result: Some(Value::List {
vals: vec![Value::test_string("found 2 at 1!")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("found 2 at 1!")],
Span::test_data(),
)),
},
Example {
example: r#"[1 2 3] | each --keep-empty {|e| if $e == 2 { "found 2!"} }"#,
description: "Iterate over each element, keeping null results",
result: Some(Value::List {
vals: stream_test_2,
span: Span::test_data(),
}),
result: Some(Value::list(stream_test_2, Span::test_data())),
},
]
}
@ -171,10 +161,7 @@ with 'transpose' first."#
Err(ShellError::Break(_)) => None,
Err(error) => {
let error = chain_error_with_input(error, x_is_error, input_span);
Some(Value::Error {
error: Box::new(error),
span: input_span,
})
Some(Value::error(error, input_span))
}
}
})
@ -195,12 +182,7 @@ with 'transpose' first."#
Ok(x) => x,
Err(ShellError::Continue(v)) => return Some(Value::nothing(v)),
Err(ShellError::Break(_)) => return None,
Err(err) => {
return Some(Value::Error {
error: Box::new(err),
span,
})
}
Err(err) => return Some(Value::error(err, span)),
};
if let Some(var) = block.signature.get_positional(0) {
@ -224,12 +206,8 @@ with 'transpose' first."#
Err(ShellError::Continue(v)) => Some(Value::nothing(v)),
Err(ShellError::Break(_)) => None,
Err(error) => {
let error =
Box::new(chain_error_with_input(error, x_is_error, input_span));
Some(Value::Error {
error,
span: input_span,
})
let error = chain_error_with_input(error, x_is_error, input_span);
Some(Value::error(error, input_span))
}
}
})

View File

@ -31,8 +31,8 @@ impl Command for Enumerate {
vec![Example {
description: "Add an index to each element of a list",
example: r#"[a, b, c] | enumerate "#,
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["index".into(), "item".into()],
vals: vec![Value::test_int(0), Value::test_string("a")],
@ -46,8 +46,8 @@ impl Command for Enumerate {
vals: vec![Value::test_int(2), Value::test_string("c")],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}

View File

@ -43,18 +43,18 @@ impl Command for Every {
Example {
example: "[1 2 3 4 5] | every 2",
description: "Get every second row",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(3), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[1 2 3 4 5] | every 2 --skip",
description: "Skip every second row",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(4)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(4)],
Span::test_data(),
)),
},
]
}

View File

@ -101,10 +101,10 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
None
}
}
Err(error) => Some(Value::Error {
error: Box::new(chain_error_with_input(error, x.is_error(), x.span())),
span: x.span(),
}),
Err(error) => Some(Value::error(
chain_error_with_input(error, x.is_error(), x.span()),
x.span(),
)),
}
})
.into_pipeline_data(ctrlc)),
@ -120,12 +120,7 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
let x = match x {
Ok(x) => x,
Err(err) => {
return Some(Value::Error {
error: Box::new(err),
span,
})
}
Err(err) => return Some(Value::error(err, span)),
};
if let Some(var) = block.signature.get_positional(0) {
@ -150,10 +145,10 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
None
}
}
Err(error) => Some(Value::Error {
error: Box::new(chain_error_with_input(error, x.is_error(), x.span())),
span: x.span(),
}),
Err(error) => Some(Value::error(
chain_error_with_input(error, x.is_error(), x.span()),
x.span(),
)),
}
})
.into_pipeline_data(ctrlc)),
@ -184,10 +179,10 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
None
}
}
Err(error) => Some(Value::Error {
error: Box::new(chain_error_with_input(error, x.is_error(), x.span())),
span: x.span(),
}),
Err(error) => Some(Value::error(
chain_error_with_input(error, x.is_error(), x.span()),
x.span(),
)),
}
.into_pipeline_data(ctrlc))
}
@ -200,57 +195,54 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
Example {
description: "Filter items of a list according to a condition",
example: "[1 2] | filter {|x| $x > 1}",
result: Some(Value::List {
vals: vec![Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(vec![Value::test_int(2)], Span::test_data())),
},
Example {
description: "Filter rows of a table according to a condition",
example: "[{a: 1} {a: 2}] | filter {|x| $x.a > 1}",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_int(2)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Filter rows of a table according to a stored condition",
example: "let cond = {|x| $x.a > 1}; [{a: 1} {a: 2}] | filter $cond",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_int(2)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Filter items of a range according to a condition",
example: "9..13 | filter {|el| $el mod 2 != 0}",
result: Some(Value::List {
vals: vec![Value::test_int(9), Value::test_int(11), Value::test_int(13)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(9), Value::test_int(11), Value::test_int(13)],
Span::test_data(),
)),
},
Example {
description: "List all numbers above 3, using an existing closure condition",
example: "let a = {$in > 3}; [1, 2, 5, 6] | filter $a",
result: None, // TODO: This should work
// result: Some(Value::List {
// vals: vec![
// result: Some(Value::list(
// vec![
// Value::Int {
// val: 5,
// span: Span::test_data(),
// Span::test_data(),
// },
// Value::Int {
// val: 6,
// span: Span::test_data(),
// },
// ],
// span: Span::test_data(),
// Span::test_data(),
// }),
},
]

View File

@ -87,60 +87,60 @@ impl Command for Find {
Example {
description: "Search a number or a file size in a list of numbers",
example: r#"[1 5 3kb 4 3Mb] | find 5 3kb"#,
result: Some(Value::List {
vals: vec![Value::test_int(5), Value::test_filesize(3000)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(5), Value::test_filesize(3000)],
Span::test_data(),
)),
},
Example {
description: "Search a char in a list of string",
example: r#"[moe larry curly] | find l"#,
result: Some(Value::List {
vals: vec![Value::test_string("larry"), Value::test_string("curly")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("larry"), Value::test_string("curly")],
Span::test_data(),
)),
},
Example {
description: "Find using regex",
example: r#"[abc bde arc abf] | find --regex "ab""#,
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("abc".to_string()),
Value::test_string("abf".to_string()),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Find using regex case insensitive",
example: r#"[aBc bde Arc abf] | find --regex "ab" -i"#,
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("aBc".to_string()),
Value::test_string("abf".to_string()),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Find value in records using regex",
example: r#"[[version name]; ['0.1.0' nushell] ['0.1.1' fish] ['0.2.0' zsh]] | find -r "nu""#,
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["version".to_string(), "name".to_string()],
vals: vec![
Value::test_string("0.1.0"),
Value::test_string("nushell".to_string()),
],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Find inverted values in records using regex",
example: r#"[[version name]; ['0.1.0' nushell] ['0.1.1' fish] ['0.2.0' zsh]] | find -r "nu" --invert"#,
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["version".to_string(), "name".to_string()],
vals: vec![
@ -156,30 +156,30 @@ impl Command for Find {
],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Find value in list using regex",
example: r#"[["Larry", "Moe"], ["Victor", "Marina"]] | find -r "rr""#,
result: Some(Value::List {
vals: vec![Value::List {
vals: vec![Value::test_string("Larry"), Value::test_string("Moe")],
span: Span::test_data(),
}],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::list(
vec![Value::test_string("Larry"), Value::test_string("Moe")],
Span::test_data(),
)],
Span::test_data(),
)),
},
Example {
description: "Find inverted values in records using regex",
example: r#"[["Larry", "Moe"], ["Victor", "Marina"]] | find -r "rr" --invert"#,
result: Some(Value::List {
vals: vec![Value::List {
vals: vec![Value::test_string("Victor"), Value::test_string("Marina")],
span: Span::test_data(),
}],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::list(
vec![Value::test_string("Victor"), Value::test_string("Marina")],
Span::test_data(),
)],
Span::test_data(),
)),
},
Example {
description: "Remove ANSI sequences from result",
@ -190,8 +190,8 @@ impl Command for Find {
description: "Find and highlight text in specific columns",
example:
"[[col1 col2 col3]; [moe larry curly] [larry curly moe]] | find moe -c [col1]",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
vals: vec![
Value::test_string(
@ -202,8 +202,8 @@ impl Command for Find {
Value::test_string("curly".to_string()),
],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -224,7 +224,7 @@ impl Command for Find {
if let Some(regex) = regex {
find_with_regex(regex, engine_state, stack, call, input)
} else {
let input = split_string_if_multiline(input);
let input = split_string_if_multiline(input, call.head);
find_with_rest_and_highlight(engine_state, stack, call, input)
}
}
@ -331,10 +331,7 @@ fn highlight_terms_in_record_with_search_columns(
highlight_search_string(&val_str, term_str, &string_style, &highlight_style)
.unwrap_or_else(|_| string_style.paint(term_str).to_string());
Value::String {
val: highlighted_str,
span,
}
Value::string(highlighted_str, span)
})
.map(|v| v.unwrap_or_else(|v| v));
@ -388,17 +385,20 @@ fn find_with_rest_and_highlight(
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(_, _) => input
.map(
move |mut x| match &mut x {
Value::Record { val, span } => highlight_terms_in_record_with_search_columns(
&cols_to_search_in_map,
val,
*span,
&config,
&terms,
string_style,
highlight_style,
),
_ => x,
move |mut x| {
let span = x.span();
match &mut x {
Value::Record { val, .. } => highlight_terms_in_record_with_search_columns(
&cols_to_search_in_map,
val,
span,
&config,
&terms,
string_style,
highlight_style,
),
_ => x,
}
},
ctrlc.clone(),
)?
@ -417,17 +417,20 @@ fn find_with_rest_and_highlight(
),
PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream(
stream
.map(move |mut x| match &mut x {
Value::Record { val, span } => highlight_terms_in_record_with_search_columns(
&cols_to_search_in_map,
val,
*span,
&config,
&terms,
string_style,
highlight_style,
),
_ => x,
.map(move |mut x| {
let span = x.span();
match &mut x {
Value::Record { val, .. } => highlight_terms_in_record_with_search_columns(
&cols_to_search_in_map,
val,
span,
&config,
&terms,
string_style,
highlight_style,
),
_ => x,
}
})
.filter(move |value| {
value_should_be_printed(
@ -451,42 +454,45 @@ fn find_with_rest_and_highlight(
let mut output: Vec<Value> = vec![];
for filter_val in stream {
match filter_val {
Ok(value) => match value {
Value::String { val, span } => {
let split_char = if val.contains("\r\n") { "\r\n" } else { "\n" };
Ok(value) => {
let span = value.span();
match value {
Value::String { val, .. } => {
let split_char = if val.contains("\r\n") { "\r\n" } else { "\n" };
for line in val.split(split_char) {
for term in lower_terms.iter() {
let term_str = term.into_string("", &filter_config);
let lower_val = line.to_lowercase();
if lower_val
.contains(&term.into_string("", &config).to_lowercase())
{
output.push(Value::String {
val: highlight_search_string(
line,
&term_str,
&string_style,
&highlight_style,
)?,
span,
})
for line in val.split(split_char) {
for term in lower_terms.iter() {
let term_str = term.into_string("", &filter_config);
let lower_val = line.to_lowercase();
if lower_val
.contains(&term.into_string("", &config).to_lowercase())
{
output.push(Value::string(
highlight_search_string(
line,
&term_str,
&string_style,
&highlight_style,
)?,
span,
))
}
}
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => return Err(*error),
other => {
return Err(ShellError::UnsupportedInput(
"unsupported type from raw stream".into(),
format!("input: {:?}", other.get_type()),
span,
// This line requires the Value::Error match above.
other.span(),
));
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => return Err(*error),
other => {
return Err(ShellError::UnsupportedInput(
"unsupported type from raw stream".into(),
format!("input: {:?}", other.get_type()),
span,
// This line requires the Value::Error match above.
other.span(),
));
}
},
}
// Propagate any errors that were in the stream
Err(e) => return Err(e),
};
@ -593,21 +599,17 @@ mod tests {
}
}
fn split_string_if_multiline(input: PipelineData) -> PipelineData {
fn split_string_if_multiline(input: PipelineData, head_span: Span) -> PipelineData {
let span = input.span().unwrap_or(head_span);
match input {
PipelineData::Value(Value::String { ref val, span }, _) => {
PipelineData::Value(Value::String { ref val, .. }, _) => {
if val.contains('\n') {
Value::List {
vals: {
val.lines()
.map(|s| Value::String {
val: s.to_string(),
span,
})
.collect()
},
Value::list(
val.lines()
.map(|s| Value::string(s.to_string(), span))
.collect(),
span,
}
)
.into_pipeline_data()
.set_metadata(input.metadata())
} else {

View File

@ -68,18 +68,15 @@ impl Command for First {
Example {
description: "Return the first 2 items of a list/table",
example: "[1 2 3] | first 2",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(2)],
Span::test_data(),
)),
},
Example {
description: "Return the first 2 bytes of a binary value",
example: "0x[01 23 45] | first 2",
result: Some(Value::Binary {
val: vec![0x01, 0x23],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0x01, 0x23], Span::test_data())),
},
]
}
@ -115,63 +112,60 @@ fn first_helper(
}
match input {
PipelineData::Value(val, _) => match val {
Value::List { vals, .. } => {
if return_single_element {
if vals.is_empty() {
Err(ShellError::AccessEmptyContent { span: head })
PipelineData::Value(val, _) => {
let span = val.span();
match val {
Value::List { vals, .. } => {
if return_single_element {
if vals.is_empty() {
Err(ShellError::AccessEmptyContent { span: head })
} else {
Ok(vals[0].clone().into_pipeline_data())
}
} else {
Ok(vals[0].clone().into_pipeline_data())
Ok(vals
.into_iter()
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
} else {
Ok(vals
.into_iter()
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
}
Value::Binary { val, span } => {
if return_single_element {
if val.is_empty() {
Err(ShellError::AccessEmptyContent { span: head })
Value::Binary { val, .. } => {
if return_single_element {
if val.is_empty() {
Err(ShellError::AccessEmptyContent { span: head })
} else {
Ok(PipelineData::Value(
Value::int(val[0] as i64, span),
metadata,
))
}
} else {
Ok(PipelineData::Value(
Value::Int {
val: val[0] as i64,
span,
},
metadata,
))
let slice: Vec<u8> = val.into_iter().take(rows_desired).collect();
Ok(PipelineData::Value(Value::binary(slice, span), metadata))
}
} else {
let slice: Vec<u8> = val.into_iter().take(rows_desired).collect();
Ok(PipelineData::Value(
Value::Binary { val: slice, span },
metadata,
))
}
}
Value::Range { val, .. } => {
if return_single_element {
Ok(val.from.into_pipeline_data())
} else {
Ok(val
.into_range_iter(ctrlc.clone())?
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
Value::Range { val, .. } => {
if return_single_element {
Ok(val.from.into_pipeline_data())
} else {
Ok(val
.into_range_iter(ctrlc.clone())?
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary or range".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary or range".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
},
}
PipelineData::ListStream(mut ls, metadata) => {
if return_single_element {
if let Some(v) = ls.next() {

View File

@ -52,8 +52,8 @@ impl Command for Flatten {
Example {
description: "flatten a table",
example: "[[N, u, s, h, e, l, l]] | flatten ",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("N"),
Value::test_string("u"),
Value::test_string("s"),
@ -61,8 +61,8 @@ impl Command for Flatten {
Value::test_string("e"),
Value::test_string("l"),
Value::test_string("l")],
span: Span::test_data()
})
Span::test_data()
))
},
Example {
description: "flatten a table, get the first item",
@ -82,17 +82,17 @@ impl Command for Flatten {
Example {
description: "Flatten inner table",
example: "{ a: b, d: [ 1 2 3 4 ], e: [ 4 3 ] } | flatten d --all",
result: Some(Value::List{
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["a".to_string(), "d".to_string(), "e".to_string()],
vals: vec![
Value::test_string("b"),
Value::test_int(1),
Value::List {
vals: vec![Value::test_int(4), Value::test_int(3)],
span: Span::test_data(),
},
Value::list(
vec![Value::test_int(4), Value::test_int(3)],
Span::test_data(),
),
],
}),
Value::test_record(Record {
@ -100,10 +100,10 @@ impl Command for Flatten {
vals: vec![
Value::test_string("b"),
Value::test_int(2),
Value::List {
vals: vec![Value::test_int(4), Value::test_int(3)],
span: Span::test_data(),
},
Value::list(
vec![Value::test_int(4), Value::test_int(3)],
Span::test_data(),
),
],
}),
Value::test_record(Record {
@ -111,10 +111,10 @@ impl Command for Flatten {
vals: vec![
Value::test_string("b"),
Value::test_int(3),
Value::List {
vals: vec![Value::test_int(4), Value::test_int(3)],
span: Span::test_data(),
},
Value::list(
vec![Value::test_int(4), Value::test_int(3)],
Span::test_data(),
),
],
}),
Value::test_record(Record {
@ -122,15 +122,15 @@ impl Command for Flatten {
vals: vec![
Value::test_string("b"),
Value::test_int(4),
Value::List {
vals: vec![Value::test_int(4), Value::test_int(3)],
span: Span::test_data()
}
Value::list(
vec![Value::test_int(4), Value::test_int(3)],
Span::test_data()
)
],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
}
]
}
@ -187,15 +187,15 @@ fn flat_value(columns: &[CellPath], item: &Value, name_tag: Span, all: bool) ->
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => return vec![item.clone()],
other => {
return vec![Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
return vec![Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(),
wrong_type: other.get_type().to_string(),
dst_span: name_tag,
src_span: other.span(),
}),
span: name_tag,
}];
},
name_tag,
)];
}
};
@ -204,6 +204,7 @@ fn flat_value(columns: &[CellPath], item: &Value, name_tag: Span, all: bool) ->
for (column_index, (column, value)) in record.iter().enumerate() {
let column_requested = columns.iter().find(|c| c.into_string() == *column);
let need_flatten = { columns.is_empty() || column_requested.is_some() };
let span = value.span();
match value {
Value::Record { val, .. } => {
@ -221,14 +222,14 @@ fn flat_value(columns: &[CellPath], item: &Value, name_tag: Span, all: bool) ->
out.insert(column.to_string(), value.clone());
}
}
Value::List { vals, span } if all && vals.iter().all(|f| f.as_record().is_ok()) => {
Value::List { vals, .. } if all && vals.iter().all(|f| f.as_record().is_ok()) => {
if need_flatten && inner_table.is_some() {
return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput(
return vec![Value::error( ShellError::UnsupportedInput(
"can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(),
"value originates from here".into(),
s,
*span
)), span: *span}
span
), span)
];
}
// it's a table (a list of record, we can flatten inner record)
@ -257,15 +258,15 @@ fn flat_value(columns: &[CellPath], item: &Value, name_tag: Span, all: bool) ->
out.insert(column.to_string(), value.clone());
}
}
Value::List { vals: values, span } => {
Value::List { vals: values, .. } => {
if need_flatten && inner_table.is_some() {
return vec![Value::Error{ error: Box::new(ShellError::UnsupportedInput(
"can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(),
"value originates from here".into(),
s,
*span
)), span: *span}
];
return vec![Value::error( ShellError::UnsupportedInput(
"can only flatten one inner list at a time. tried flattening more than one column with inner lists... but is flattened already".to_string(),
"value originates from here".into(),
s,
span
), span)
];
}
if !columns.is_empty() {
@ -370,7 +371,7 @@ fn flat_value(columns: &[CellPath], item: &Value, name_tag: Span, all: bool) ->
}
expanded
} else if item.as_list().is_ok() {
if let Value::List { vals, span: _ } = item {
if let Value::List { vals, .. } = item {
vals.to_vec()
} else {
vec![]

View File

@ -106,10 +106,10 @@ If multiple cell paths are given, this will produce a list of values."#
Example {
description: "Get a column from a table",
example: "[{A: A0}] | get A",
result: Some(Value::List {
vals: vec![Value::test_string("A0")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("A0")],
Span::test_data(),
)),
},
Example {
description: "Get a cell from a table",

View File

@ -34,23 +34,20 @@ impl Command for Group {
fn examples(&self) -> Vec<Example> {
let stream_test_1 = vec![
Value::List {
vals: vec![Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
},
Value::List {
vals: vec![Value::test_int(3), Value::test_int(4)],
span: Span::test_data(),
},
Value::list(
vec![Value::test_int(1), Value::test_int(2)],
Span::test_data(),
),
Value::list(
vec![Value::test_int(3), Value::test_int(4)],
Span::test_data(),
),
];
vec![Example {
example: "[1 2 3 4] | group 2",
description: "Group the a list by pairs",
result: Some(Value::List {
vals: stream_test_1,
span: Span::test_data(),
}),
result: Some(Value::list(stream_test_1, Span::test_data())),
}]
}
@ -112,10 +109,7 @@ impl Iterator for EachGroupIterator {
return None;
}
Some(Value::List {
vals: group,
span: self.span,
})
Some(Value::list(group, self.span))
}
}

View File

@ -71,17 +71,17 @@ impl Command for GroupBy {
result: Some(Value::test_record(Record {
cols: vec!["txt".to_string(), "csv".to_string()],
vals: vec![
Value::List {
vals: vec![
Value::list(
vec![
Value::test_string("foo.txt"),
Value::test_string("baz.txt"),
],
span: Span::test_data(),
},
Value::List {
vals: vec![Value::test_string("bar.csv")],
span: Span::test_data(),
},
Span::test_data(),
),
Value::list(
vec![Value::test_string("bar.csv")],
Span::test_data(),
),
],
})),
},
@ -92,23 +92,23 @@ impl Command for GroupBy {
result: Some(Value::test_record(Record {
cols: vec!["1".to_string(), "3".to_string(), "2".to_string()],
vals: vec![
Value::List {
vals: vec![
Value::list(
vec![
Value::test_string("1"),
Value::test_string("1"),
Value::test_string("1"),
Value::test_string("1"),
],
span: Span::test_data(),
},
Value::List {
vals: vec![Value::test_string("3"), Value::test_string("3")],
span: Span::test_data(),
},
Value::List {
vals: vec![Value::test_string("2")],
span: Span::test_data(),
},
Span::test_data(),
),
Value::list(
vec![Value::test_string("3"), Value::test_string("3")],
Span::test_data(),
),
Value::list(
vec![Value::test_string("2")],
Span::test_data(),
),
],
})),
},
@ -138,18 +138,24 @@ pub fn group_by(
}
let group_value = match grouper {
Some(Value::CellPath { val, span }) => group_cell_path(val, values, span)?,
Some(Value::Block { .. }) | Some(Value::Closure { .. }) => {
let block: Option<Closure> = call.opt(engine_state, stack, 0)?;
group_closure(&values, span, block, stack, engine_state, call)?
Some(v) => {
let span = v.span();
match v {
Value::CellPath { val, .. } => group_cell_path(val, values, span)?,
Value::Block { .. } | Value::Closure { .. } => {
let block: Option<Closure> = call.opt(engine_state, stack, 0)?;
group_closure(&values, span, block, stack, engine_state, call)?
}
_ => {
return Err(ShellError::TypeMismatch {
err_message: "unsupported grouper type".to_string(),
span,
})
}
}
}
None => group_no_grouper(values, span)?,
_ => {
return Err(ShellError::TypeMismatch {
err_message: "unsupported grouper type".to_string(),
span,
})
}
};
Ok(PipelineData::Value(group_value, None))
@ -213,10 +219,7 @@ fn group_closure(
) -> Result<Value, ShellError> {
let error_key = "error";
let mut keys: Vec<Result<String, ShellError>> = vec![];
let value_list = Value::List {
vals: values.clone(),
span,
};
let value_list = Value::list(values.clone(), span);
for value in values {
if let Some(capture_block) = &block {

View File

@ -36,8 +36,8 @@ impl Command for Headers {
Example {
description: "Sets the column names for a table created by `split column`",
example: r#""a b c|1 2 3" | split row "|" | split column " " | headers"#,
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: columns.clone(),
vals: vec![
Value::test_string("1"),
@ -45,14 +45,14 @@ impl Command for Headers {
Value::test_string("3"),
],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Columns which don't have data in their first row are removed",
example: r#""a b c|1 2 3|1 2 3 4" | split row "|" | split column " " | headers"#,
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: columns.clone(),
vals: vec![
@ -70,8 +70,8 @@ impl Command for Headers {
],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -98,8 +98,9 @@ fn replace_headers(
old_headers: &[String],
new_headers: &[String],
) -> Result<Value, ShellError> {
let span = value.span();
match value {
Value::Record { val, span } => Ok(Value::record(
Value::Record { val, .. } => Ok(Value::record(
val.into_iter()
.filter_map(|(col, val)| {
old_headers
@ -110,14 +111,14 @@ fn replace_headers(
.collect(),
span,
)),
Value::List { vals, span } => {
Value::List { vals, .. } => {
let vals = vals
.into_iter()
.skip(1)
.map(|value| replace_headers(value, old_headers, new_headers))
.collect::<Result<Vec<Value>, ShellError>>()?;
Ok(Value::List { vals, span })
Ok(Value::list(vals, span))
}
_ => Err(ShellError::TypeMismatch {
err_message: "record".to_string(),
@ -129,11 +130,11 @@ fn replace_headers(
fn is_valid_header(value: &Value) -> bool {
matches!(
value,
Value::Nothing { span: _ }
| Value::String { val: _, span: _ }
| Value::Bool { val: _, span: _ }
| Value::Float { val: _, span: _ }
| Value::Int { val: _, span: _ }
Value::Nothing { .. }
| Value::String { val: _, .. }
| Value::Bool { val: _, .. }
| Value::Float { val: _, .. }
| Value::Int { val: _, .. }
)
}
@ -141,6 +142,7 @@ fn extract_headers(
value: &Value,
config: &Config,
) -> Result<(Vec<String>, Vec<String>), ShellError> {
let span = value.span();
match value {
Value::Record { val: record, .. } => {
for v in &record.vals {
@ -170,7 +172,7 @@ fn extract_headers(
Ok((old_headers, new_headers))
}
Value::List { vals, span } => vals
Value::List { vals, .. } => vals
.iter()
.map(|value| extract_headers(value, config))
.next()
@ -178,7 +180,7 @@ fn extract_headers(
ShellError::GenericError(
"Found empty list".to_string(),
"unable to extract headers".to_string(),
Some(*span),
Some(span),
None,
Vec::new(),
)

View File

@ -72,19 +72,19 @@ impl Command for Insert {
Example {
description: "Insert a new column into a table, populating all rows",
example: "[[project, lang]; ['Nushell', 'Rust']] | insert type 'shell'",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list (
vec![Value::test_record(Record {
cols: vec!["project".into(), "lang".into(), "type".into()],
vals: vec![Value::test_string("Nushell"), Value::test_string("Rust"), Value::test_string("shell")],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Insert a column with values equal to their row index, plus the value of 'foo' in each row",
example: "[[foo]; [7] [8] [9]] | enumerate | insert bar {|e| $e.item.foo + $e.index } | flatten",
result: Some(Value::List {
vals: vec![
result: Some(Value::list (
vec![
Value::test_record(Record {
cols: vec!["index".into(), "foo".into(), "bar".into()],
vals: vec![
@ -110,8 +110,8 @@ impl Command for Insert {
],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}
}
@ -173,18 +173,12 @@ fn insert(
pd.into_value(span),
span,
) {
return Value::Error {
error: Box::new(e),
span,
};
return Value::error(e, span);
}
input
}
Err(e) => Value::Error {
error: Box::new(e),
span,
},
Err(e) => Value::error(e, span),
}
},
ctrlc,
@ -198,7 +192,7 @@ fn insert(
if let Some(v) = input.next() {
pre_elems.push(v);
} else {
pre_elems.push(Value::Nothing { span })
pre_elems.push(Value::nothing(span))
}
}
@ -215,10 +209,7 @@ fn insert(
if let Err(e) =
input.insert_data_at_cell_path(&cell_path.members, replacement, span)
{
return Value::Error {
error: Box::new(e),
span,
};
return Value::error(e, span);
}
input

View File

@ -87,10 +87,7 @@ impl Command for Items {
Err(ShellError::Break(_)) => None,
Err(error) => {
let error = chain_error_with_input(error, false, input_span);
Some(Value::Error {
error: Box::new(error),
span,
})
Some(Value::error(error, span))
}
}
};
@ -129,13 +126,13 @@ impl Command for Items {
example:
"{ new: york, san: francisco } | items {|key, value| echo $'($key) ($value)' }",
description: "Iterate over each key-value pair of a record",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("new york"),
Value::test_string("san francisco"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}
}

View File

@ -110,26 +110,13 @@ impl Command for Join {
vec![Example {
description: "Join two tables",
example: "[{a: 1 b: 2}] | join [{a: 1 c: 3}] a",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["a".into(), "b".into(), "c".into()],
vals: vec![
Value::Int {
val: 1,
span: Span::test_data(),
},
Value::Int {
val: 2,
span: Span::test_data(),
},
Value::Int {
val: 3,
span: Span::test_data(),
},
],
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}
}
@ -253,7 +240,7 @@ fn join(
span,
);
}
Value::List { vals: result, span }
Value::list(result, span)
}
// Join rows of `this` (a nushell table) to rows of `other` (a lookup-table

View File

@ -54,10 +54,10 @@ impl Command for Last {
Example {
example: "[1,2,3] | last 2",
description: "Return the last 2 items of a list/table",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(3)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(3)],
Span::test_data(),
)),
},
Example {
example: "[1,2,3] | last",
@ -67,10 +67,7 @@ impl Command for Last {
Example {
example: "0x[01 23 45] | last 2",
description: "Return the last 2 bytes of a binary value",
result: Some(Value::Binary {
val: vec![0x23, 0x45],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0x23, 0x45], Span::test_data())),
},
]
}
@ -129,55 +126,56 @@ impl Command for Last {
Ok(buf.into_pipeline_data(ctrlc).set_metadata(metadata))
}
}
PipelineData::Value(val, _) => match val {
Value::List { vals, .. } => {
if return_single_element {
if let Some(v) = vals.last() {
Ok(v.clone().into_pipeline_data())
PipelineData::Value(val, _) => {
let val_span = val.span();
match val {
Value::List { vals, .. } => {
if return_single_element {
if let Some(v) = vals.last() {
Ok(v.clone().into_pipeline_data())
} else {
Err(ShellError::AccessEmptyContent { span: head })
}
} else {
Err(ShellError::AccessEmptyContent { span: head })
Ok(vals
.into_iter()
.rev()
.take(rows_desired)
.rev()
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
} else {
Ok(vals
.into_iter()
.rev()
.take(rows_desired)
.rev()
.into_pipeline_data(ctrlc)
.set_metadata(metadata))
}
}
Value::Binary { val, span } => {
if return_single_element {
if let Some(b) = val.last() {
Value::Binary { val, .. } => {
if return_single_element {
if let Some(b) = val.last() {
Ok(PipelineData::Value(
Value::int(*b as i64, val_span),
metadata,
))
} else {
Err(ShellError::AccessEmptyContent { span: head })
}
} else {
let slice: Vec<u8> =
val.into_iter().rev().take(rows_desired).rev().collect();
Ok(PipelineData::Value(
Value::Int {
val: *b as i64,
span,
},
Value::binary(slice, val_span),
metadata,
))
} else {
Err(ShellError::AccessEmptyContent { span: head })
}
} else {
let slice: Vec<u8> =
val.into_iter().rev().take(rows_desired).rev().collect();
Ok(PipelineData::Value(
Value::Binary { val: slice, span },
metadata,
))
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary or range".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary or range".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
},
}
PipelineData::ExternalStream { span, .. } => {
Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary or range".into(),

View File

@ -56,13 +56,14 @@ impl Command for Length {
}
fn length_row(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
let span = input.span().unwrap_or(call.head);
match input {
PipelineData::Value(Value::Nothing { .. }, ..) => {
Ok(Value::int(0, call.head).into_pipeline_data())
}
// I added this here because input_output_type() wasn't catching a record
// being sent in as input from echo. e.g. "echo {a:1 b:2} | length"
PipelineData::Value(Value::Record { span, .. }, ..) => {
PipelineData::Value(Value::Record { .. }, ..) => {
Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, and table".into(),
wrong_type: "record".into(),

View File

@ -27,7 +27,6 @@ impl Command for Lines {
.switch("skip-empty", "skip empty lines", Some('s'))
.category(Category::Filters)
}
fn run(
&self,
engine_state: &EngineState,
@ -42,12 +41,13 @@ impl Command for Lines {
// match \r\n or \n
static LINE_BREAK_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"\r\n|\n").expect("unable to compile regex"));
let span = input.span().unwrap_or(call.head);
match input {
#[allow(clippy::needless_collect)]
// Collect is needed because the string may not live long enough for
// the Rc structure to continue using it. If split could take ownership
// of the split values, then this wouldn't be needed
PipelineData::Value(Value::String { val, span }, ..) => {
PipelineData::Value(Value::String { val, .. }, ..) => {
let mut lines = LINE_BREAK_REGEX
.split(&val)
.map(|s| s.to_string())
@ -76,7 +76,8 @@ impl Command for Lines {
let iter = stream
.into_iter()
.filter_map(move |value| {
if let Value::String { val, span } = value {
let span = value.span();
if let Value::String { val, .. } = value {
let mut lines = LINE_BREAK_REGEX
.split(&val)
.filter_map(|s| {
@ -96,11 +97,7 @@ impl Command for Lines {
}
}
Some(
lines
.into_iter()
.map(move |x| Value::String { val: x, span }),
)
Some(lines.into_iter().map(move |x| Value::string(x, span)))
} else {
None
}
@ -129,10 +126,7 @@ impl Command for Lines {
.enumerate()
.map(move |(_idx, x)| match x {
Ok(x) => x,
Err(err) => Value::Error {
error: Box::new(err),
span: head,
},
Err(err) => Value::error(err, head),
})
.into_pipeline_data(ctrlc)),
}
@ -142,10 +136,10 @@ impl Command for Lines {
vec![Example {
description: "Split multi-line string into lines",
example: r#"$"two\nlines" | lines"#,
result: Some(Value::List {
vals: vec![Value::test_string("two"), Value::test_string("lines")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("two"), Value::test_string("lines")],
Span::test_data(),
)),
}]
}
}
@ -175,10 +169,7 @@ impl Iterator for RawStreamLinesAdapter {
continue;
}
return Some(Ok(Value::String {
val: s,
span: self.span,
}));
return Some(Ok(Value::string(s, self.span)));
} else {
// inner is complete, feed out remaining state
if self.inner_complete {
@ -198,9 +189,10 @@ impl Iterator for RawStreamLinesAdapter {
if let Some(result) = self.inner.next() {
match result {
Ok(v) => {
let span = v.span();
match v {
// TODO: Value::Binary support required?
Value::String { val, span } => {
Value::String { val, .. } => {
self.span = span;
let mut lines = LINE_BREAK_REGEX

View File

@ -46,8 +46,8 @@ repeating this process with row 1, and so on."#
Example {
example: "[a b c] | wrap name | merge ( [1 2 3] | wrap index )",
description: "Add an 'index' column to the input table",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["name".to_string(), "index".to_string()],
vals: vec![Value::test_string("a"), Value::test_int(1)],
@ -61,8 +61,8 @@ repeating this process with row 1, and so on."#
vals: vec![Value::test_string("c"), Value::test_int(3)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "{a: 1, b: 2} | merge {c: 3}",
@ -75,13 +75,13 @@ repeating this process with row 1, and so on."#
Example {
example: "[{columnA: A0 columnB: B0}] | merge [{columnA: 'A0*'}]",
description: "Merge two tables, overwriting overlapping columns",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["columnA".to_string(), "columnB".to_string()],
vals: vec![Value::test_string("A0*"), Value::test_string("B0")],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -113,16 +113,10 @@ repeating this process with row 1, and so on."#
.map(move |inp| match (inp.as_record(), table_iter.next()) {
(Ok(inp), Some(to_merge)) => match to_merge.as_record() {
Ok(to_merge) => Value::record(do_merge(inp, to_merge), call.head),
Err(error) => Value::Error {
error: Box::new(error),
span: call.head,
},
Err(error) => Value::error(error, call.head),
},
(_, None) => inp,
(Err(error), _) => Value::Error {
error: Box::new(error),
span: call.head,
},
(Err(error), _) => Value::error(error, call.head),
});
if let Some(md) = metadata {

View File

@ -52,8 +52,8 @@ impl Command for Move {
example: "[[name value index]; [foo a 1] [bar b 2] [baz c 3]] | move index --before name",
description: "Move a column before the first column",
result:
Some(Value::List {
vals: vec![
Some(Value::list (
vec![
Value::test_record(Record {
cols: vec!["index".to_string(), "name".to_string(), "value".to_string()],
vals: vec![Value::test_int(1), Value::test_string("foo"), Value::test_string("a")],
@ -67,15 +67,15 @@ impl Command for Move {
vals: vec![Value::test_int(3), Value::test_string("baz"), Value::test_string("c")],
}),
],
span: Span::test_data(),
})
Span::test_data(),
))
},
Example {
example: "[[name value index]; [foo a 1] [bar b 2] [baz c 3]] | move value name --after index",
description: "Move multiple columns after the last column and reorder them",
result:
Some(Value::List {
vals: vec![
Some(Value::list (
vec![
Value::test_record(Record {
cols: vec!["index".to_string(), "value".to_string(), "name".to_string()],
vals: vec![Value::test_int(1), Value::test_string("a"), Value::test_string("foo")],
@ -89,8 +89,8 @@ impl Command for Move {
vals: vec![Value::test_int(3), Value::test_string("c"), Value::test_string("baz")],
}),
],
span: Span::test_data(),
})
Span::test_data(),
))
},
Example {
example: "{ name: foo, value: a, index: 1 } | move name --before index",
@ -153,16 +153,10 @@ impl Command for Move {
Ok(record) => {
match move_record_columns(record, &columns, &before_or_after, call.head) {
Ok(val) => val,
Err(error) => Value::Error {
error: Box::new(error),
span: call.head,
},
Err(error) => Value::error(error, call.head),
}
}
Err(error) => Value::Error {
error: Box::new(error),
span: call.head,
},
Err(error) => Value::error(error, call.head),
});
if let Some(md) = metadata {

View File

@ -57,31 +57,31 @@ impl Command for ParEach {
Example {
example: r#"[foo bar baz] | par-each {|e| $e + '!' } | sort"#,
description: "Output can still be sorted afterward",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("bar!"),
Value::test_string("baz!"),
Value::test_string("foo!"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: r#"1..3 | enumerate | par-each {|p| update item ($p.item * 2)} | sort-by item | get item"#,
description: "Enumerate and sort-by can be used to reconstruct the original order",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(4), Value::test_int(6)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(4), Value::test_int(6)],
Span::test_data(),
)),
},
Example {
example: r#"[1 2 3] | enumerate | par-each { |e| if $e.item == 2 { $"found 2 at ($e.index)!"} }"#,
description:
"Iterate over each element, producing a list showing indexes of any 2s",
result: Some(Value::List {
vals: vec![Value::test_string("found 2 at 1!")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("found 2 at 1!")],
Span::test_data(),
)),
},
]
}
@ -154,12 +154,10 @@ impl Command for ParEach {
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(chain_error_with_input(
error, x_is_error, val_span,
)),
span: val_span,
},
Err(error) => Value::error(
chain_error_with_input(error, x_is_error, val_span),
val_span,
),
}
})
.collect::<Vec<_>>()
@ -192,12 +190,10 @@ impl Command for ParEach {
redirect_stderr,
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(chain_error_with_input(
error, x_is_error, val_span,
)),
span: val_span,
},
Err(error) => Value::error(
chain_error_with_input(error, x_is_error, val_span),
val_span,
),
}
})
.collect::<Vec<_>>()
@ -230,12 +226,10 @@ impl Command for ParEach {
redirect_stderr,
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(chain_error_with_input(
error, x_is_error, val_span,
)),
span: val_span,
},
Err(error) => Value::error(
chain_error_with_input(error, x_is_error, val_span),
val_span,
),
}
})
.collect::<Vec<_>>()
@ -252,12 +246,7 @@ impl Command for ParEach {
.map(move |x| {
let x = match x {
Ok(x) => x,
Err(err) => {
return Value::Error {
error: Box::new(err),
span,
}
}
Err(err) => return Value::error(err, span),
};
let block = engine_state.get_block(block_id);
@ -279,10 +268,7 @@ impl Command for ParEach {
redirect_stderr,
) {
Ok(v) => v.into_value(span),
Err(error) => Value::Error {
error: Box::new(error),
span,
},
Err(error) => Value::error(error, span),
}
})
.collect::<Vec<_>>()

View File

@ -45,57 +45,57 @@ only unwrap the outer list, and leave the variable's contents untouched."#
Example {
example: "0 | prepend [1 2 3]",
description: "prepend a list to an item",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(0),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: r#""a" | prepend ["b"] "#,
description: "Prepend a list of strings to a string",
result: Some(Value::List {
vals: vec![Value::test_string("b"), Value::test_string("a")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("b"), Value::test_string("a")],
Span::test_data(),
)),
},
Example {
example: "[1,2,3,4] | prepend 0",
description: "Prepend one integer item",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "[2,3,4] | prepend [0,1]",
description: "Prepend two integer items",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "[2,nu,4,shell] | prepend [0,1,rocks]",
description: "Prepend integers and strings",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_string("rocks"),
@ -104,8 +104,8 @@ only unwrap the outer list, and leave the variable's contents untouched."#
Value::test_int(4),
Value::test_string("shell"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -132,8 +132,7 @@ only unwrap the outer list, and leave the variable's contents untouched."#
fn process_value(val: Value) -> Vec<Value> {
match val {
Value::List {
vals: input_vals,
span: _,
vals: input_vals, ..
} => {
let mut output = vec![];
for input_val in input_vals {

View File

@ -38,26 +38,26 @@ impl Command for Range {
Example {
example: "[0,1,2,3,4,5] | range 4..5",
description: "Get the last 2 items",
result: Some(Value::List {
vals: vec![Value::test_int(4), Value::test_int(5)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(4), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | range (-2)..",
description: "Get the last 2 items",
result: Some(Value::List {
vals: vec![Value::test_int(4), Value::test_int(5)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(4), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | range (-3)..-2",
description: "Get the next to last 2 items",
result: Some(Value::List {
vals: vec![Value::test_int(3), Value::test_int(4)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(3), Value::test_int(4)],
Span::test_data(),
)),
},
]
}
@ -99,10 +99,7 @@ impl Command for Range {
};
if from > to {
Ok(PipelineData::Value(
Value::Nothing { span: call.head },
None,
))
Ok(PipelineData::Value(Value::nothing(call.head), None))
} else {
let iter = v.into_iter().skip(from).take(to - from + 1);
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
@ -112,10 +109,7 @@ impl Command for Range {
let to = rows_to as usize;
if from > to {
Ok(PipelineData::Value(
Value::Nothing { span: call.head },
None,
))
Ok(PipelineData::Value(Value::nothing(call.head), None))
} else {
let iter = input.into_iter().skip(from).take(to - from + 1);
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))

View File

@ -62,13 +62,13 @@ impl Command for Reject {
Example {
description: "Reject a column in a table",
example: "[[a, b]; [1, 2]] | reject a",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["b".to_string()],
vals: vec![Value::test_int(2)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Reject the specified field in a record",

View File

@ -55,35 +55,35 @@ impl Command for Rename {
Example {
description: "Rename a column",
example: "[[a, b]; [1, 2]] | rename my_column",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["my_column".to_string(), "b".to_string()],
vals: vec![Value::test_int(1), Value::test_int(2)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Rename many columns",
example: "[[a, b, c]; [1, 2, 3]] | rename eggs ham bacon",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["eggs".to_string(), "ham".to_string(), "bacon".to_string()],
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Rename a specific column",
example: "[[a, b, c]; [1, 2, 3]] | rename -c [a ham]",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["ham".to_string(), "b".to_string(), "c".to_string()],
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Rename the fields of a record",
@ -113,19 +113,23 @@ fn rename(
) -> Result<PipelineData, ShellError> {
let specified_column: Option<Vec<String>> = call.get_flag(engine_state, stack, "column")?;
// get the span for the column's name to be changed and for the given list
let (specified_col_span, list_span) = if let Some(Value::List {
vals: columns,
span: column_span,
}) = call.get_flag(engine_state, stack, "column")?
{
if columns.is_empty() {
return Err(ShellError::TypeMismatch { err_message: "The column list cannot be empty and must contain only two values: the column's name and its replacement value"
let column_flag: Option<Value> = call.get_flag(engine_state, stack, "column")?;
let (specified_col_span, list_span) = match column_flag {
Some(column_flag) => {
let column_span = column_flag.span();
match column_flag {
Value::List { vals: columns, .. } => {
if columns.is_empty() {
return Err(ShellError::TypeMismatch { err_message: "The column list cannot be empty and must contain only two values: the column's name and its replacement value"
.to_string(), span: column_span });
} else {
(Some(columns[0].span()), column_span)
} else {
(Some(columns[0].span()), column_span)
}
}
_ => (None, call.head),
}
}
} else {
(None, call.head)
None => (None, call.head),
};
if let Some(ref cols) = specified_column {
@ -155,100 +159,92 @@ fn rename(
let head_span = call.head;
input
.map(
move |item| match item {
Value::Record {
val: mut record,
span,
} => {
if let Some((engine_state, block, mut stack, env_vars, env_hidden)) =
block_info.clone()
{
for c in &mut record.cols {
stack.with_env(&env_vars, &env_hidden);
move |item| {
let span = item.span();
match item {
Value::Record {
val: mut record, ..
} => {
if let Some((engine_state, block, mut stack, env_vars, env_hidden)) =
block_info.clone()
{
for c in &mut record.cols {
stack.with_env(&env_vars, &env_hidden);
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, Value::string(c.clone(), span))
}
}
let eval_result = eval_block_with_early_return(
&engine_state,
&mut stack,
&block,
Value::string(c.clone(), span).into_pipeline_data(),
redirect_stdout,
redirect_stderr,
);
match eval_result {
Err(e) => {
return Value::Error {
error: Box::new(e),
span,
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, Value::string(c.clone(), span))
}
}
Ok(res) => match res.collect_string_strict(span) {
Err(e) => {
return Value::Error {
error: Box::new(e),
let eval_result = eval_block_with_early_return(
&engine_state,
&mut stack,
&block,
Value::string(c.clone(), span).into_pipeline_data(),
redirect_stdout,
redirect_stderr,
);
match eval_result {
Err(e) => return Value::error(e, span),
Ok(res) => match res.collect_string_strict(span) {
Err(e) => return Value::error(e, span),
Ok(new_c) => *c = new_c.0,
},
}
}
} else {
match &specified_column {
Some(c) => {
// check if the specified column to be renamed exists
if !record.cols.contains(&c[0]) {
return Value::error(
ShellError::UnsupportedInput(
format!(
"The column '{}' does not exist in the input",
&c[0]
),
"value originated from here".into(),
// Arrow 1 points at the specified column name,
specified_col_span.unwrap_or(head_span),
// Arrow 2 points at the input value.
span,
),
span,
);
}
for (idx, val) in record.cols.iter_mut().enumerate() {
if *val == c[0] {
record.cols[idx] = c[1].to_string();
break;
}
}
Ok(new_c) => *c = new_c.0,
},
}
}
} else {
match &specified_column {
Some(c) => {
// check if the specified column to be renamed exists
if !record.cols.contains(&c[0]) {
return Value::Error {
error: Box::new(ShellError::UnsupportedInput(
format!(
"The column '{}' does not exist in the input",
&c[0]
),
"value originated from here".into(),
// Arrow 1 points at the specified column name,
specified_col_span.unwrap_or(head_span),
// Arrow 2 points at the input value.
span,
)),
span,
};
}
for (idx, val) in record.cols.iter_mut().enumerate() {
if *val == c[0] {
record.cols[idx] = c[1].to_string();
break;
None => {
for (idx, val) in columns.iter().enumerate() {
if idx >= record.len() {
// skip extra new columns names if we already reached the final column
break;
}
record.cols[idx] = val.clone();
}
}
}
None => {
for (idx, val) in columns.iter().enumerate() {
if idx >= record.len() {
// skip extra new columns names if we already reached the final column
break;
}
record.cols[idx] = val.clone();
}
}
}
}
Value::record(record, span)
Value::record(record, span)
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => item.clone(),
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(),
wrong_type: other.get_type().to_string(),
dst_span: head_span,
src_span: other.span(),
},
head_span,
),
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => item.clone(),
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(),
wrong_type: other.get_type().to_string(),
dst_span: head_span,
src_span: other.span(),
}),
span: head_span,
},
},
engine_state.ctrlc.clone(),
)

View File

@ -38,21 +38,21 @@ impl Command for Reverse {
Example {
example: "[0,1,2,3] | reverse",
description: "Reverse a list",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(3),
Value::test_int(2),
Value::test_int(1),
Value::test_int(0),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "[{a: 1} {a: 2}] | reverse",
description: "Reverse a table",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_int(2)],
@ -62,8 +62,8 @@ impl Command for Reverse {
vals: vec![Value::test_int(1)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -151,13 +151,13 @@ produce a table, a list will produce a list, and a record will produce a record.
Example {
description: "Select a column in a table",
example: "[{a: a b: b}] | select a",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list (
vec![Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_string("a")]
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Select a field in a record",
@ -250,44 +250,65 @@ fn select(
};
match input {
PipelineData::Value(
Value::List {
vals: input_vals,
span,
},
metadata,
..,
) => {
let mut output = vec![];
let mut columns_with_value = Vec::new();
for input_val in input_vals {
if !columns.is_empty() {
let mut record = Record::new();
for path in &columns {
//FIXME: improve implementation to not clone
match input_val.clone().follow_cell_path(&path.members, false) {
Ok(fetcher) => {
record.push(path.into_string().replace('.', "_"), fetcher);
if !columns_with_value.contains(&path) {
columns_with_value.push(path);
PipelineData::Value(v, metadata, ..) => {
let span = v.span();
match v {
Value::List {
vals: input_vals, ..
} => {
let mut output = vec![];
let mut columns_with_value = Vec::new();
for input_val in input_vals {
if !columns.is_empty() {
let mut record = Record::new();
for path in &columns {
//FIXME: improve implementation to not clone
match input_val.clone().follow_cell_path(&path.members, false) {
Ok(fetcher) => {
record.push(path.into_string().replace('.', "_"), fetcher);
if !columns_with_value.contains(&path) {
columns_with_value.push(path);
}
}
Err(e) => {
return Err(e);
}
}
}
Err(e) => {
return Err(e);
}
output.push(Value::record(record, span))
} else {
output.push(input_val)
}
}
output.push(Value::record(record, span))
} else {
output.push(input_val)
Ok(output
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone())
.set_metadata(metadata))
}
_ => {
if !columns.is_empty() {
let mut record = Record::new();
for cell_path in columns {
// FIXME: remove clone
match v.clone().follow_cell_path(&cell_path.members, false) {
Ok(result) => {
record.push(cell_path.into_string().replace('.', "_"), result);
}
Err(e) => return Err(e),
}
}
Ok(Value::record(record, call_span)
.into_pipeline_data()
.set_metadata(metadata))
} else {
Ok(v.into_pipeline_data().set_metadata(metadata))
}
}
}
Ok(output
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone())
.set_metadata(metadata))
}
PipelineData::ListStream(stream, metadata, ..) => {
let mut values = vec![];
@ -314,27 +335,6 @@ fn select(
.into_pipeline_data(engine_state.ctrlc.clone())
.set_metadata(metadata))
}
PipelineData::Value(v, metadata, ..) => {
if !columns.is_empty() {
let mut record = Record::new();
for cell_path in columns {
// FIXME: remove clone
match v.clone().follow_cell_path(&cell_path.members, false) {
Ok(result) => {
record.push(cell_path.into_string().replace('.', "_"), result);
}
Err(e) => return Err(e),
}
}
Ok(Value::record(record, call_span)
.into_pipeline_data()
.set_metadata(metadata))
} else {
Ok(v.into_pipeline_data().set_metadata(metadata))
}
}
_ => Ok(PipelineData::empty()),
}
}

View File

@ -46,25 +46,24 @@ impl Command for Skip {
Example {
description: "Skip the first value of a list",
example: "[2 4 6 8] | skip 1",
result: Some(Value::List {
vals: vec![Value::test_int(4), Value::test_int(6), Value::test_int(8)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(4), Value::test_int(6), Value::test_int(8)],
Span::test_data(),
)),
},
Example {
description: "Skip two rows of a table",
example: "[[editions]; [2015] [2018] [2021]] | skip 2",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["editions".to_owned()],
vals: vec![Value::test_int(2021)],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
fn run(
&self,
engine_state: &EngineState,
@ -73,27 +72,33 @@ impl Command for Skip {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let n: Option<Value> = call.opt(engine_state, stack, 0)?;
let span = call.head;
let metadata = input.metadata();
let n: usize = match n {
Some(Value::Int { val, span }) => {
val.try_into().map_err(|err| ShellError::TypeMismatch {
err_message: format!("Could not convert {val} to unsigned integer: {err}"),
span,
})?
}
Some(_) => {
return Err(ShellError::TypeMismatch {
err_message: "expected integer".into(),
span,
})
Some(v) => {
let span = v.span();
match v {
Value::Int { val, .. } => {
val.try_into().map_err(|err| ShellError::TypeMismatch {
err_message: format!(
"Could not convert {val} to unsigned integer: {err}"
),
span,
})?
}
_ => {
return Err(ShellError::TypeMismatch {
err_message: "expected integer".into(),
span,
})
}
}
}
None => 1,
};
let ctrlc = engine_state.ctrlc.clone();
let input_span = input.span().unwrap_or(call.head);
match input {
PipelineData::ExternalStream {
stdout: Some(stream),
@ -130,17 +135,14 @@ impl Command for Skip {
}
}
Ok(Value::Binary {
val: output,
span: bytes_span,
}
.into_pipeline_data()
.set_metadata(metadata))
Ok(Value::binary(output, bytes_span)
.into_pipeline_data()
.set_metadata(metadata))
}
PipelineData::Value(Value::Binary { val, span }, metadata) => {
PipelineData::Value(Value::Binary { val, .. }, metadata) => {
let bytes = val.into_iter().skip(n).collect::<Vec<_>>();
Ok(Value::Binary { val: bytes, span }
Ok(Value::binary(bytes, input_span)
.into_pipeline_data()
.set_metadata(metadata))
}

View File

@ -44,24 +44,24 @@ impl Command for SkipUntil {
Example {
description: "Skip until the element is positive",
example: "[-2 0 2 -1] | skip until {|x| $x > 0 }",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(-1)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(-1)],
Span::test_data(),
)),
},
Example {
description: "Skip until the element is positive using stored condition",
example: "let cond = {|x| $x > 0 }; [-2 0 2 -1] | skip until $cond",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(-1)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(-1)],
Span::test_data(),
)),
},
Example {
description: "Skip until the field value is positive",
example: "[{a: -2} {a: 0} {a: 2} {a: -1}] | skip until {|x| $x.a > 0 }",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_int(2)],
@ -71,8 +71,8 @@ impl Command for SkipUntil {
vals: vec![Value::test_int(-1)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -44,24 +44,24 @@ impl Command for SkipWhile {
Example {
description: "Skip while the element is negative",
example: "[-2 0 2 -1] | skip while {|x| $x < 0 }",
result: Some(Value::List {
vals: vec![Value::test_int(0), Value::test_int(2), Value::test_int(-1)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(0), Value::test_int(2), Value::test_int(-1)],
Span::test_data(),
)),
},
Example {
description: "Skip while the element is negative using stored condition",
example: "let cond = {|x| $x < 0 }; [-2 0 2 -1] | skip while $cond",
result: Some(Value::List {
vals: vec![Value::test_int(0), Value::test_int(2), Value::test_int(-1)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(0), Value::test_int(2), Value::test_int(-1)],
Span::test_data(),
)),
},
Example {
description: "Skip while the field value is negative",
example: "[{a: -2} {a: 0} {a: 2} {a: -1}] | skip while {|x| $x.a < 0 }",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_int(0)],
@ -75,8 +75,8 @@ impl Command for SkipWhile {
vals: vec![Value::test_int(-1)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -49,66 +49,66 @@ impl Command for Sort {
Example {
example: "[2 0 1] | sort",
description: "sort the list by increasing value",
result: Some(Value::List {
vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
Span::test_data(),
)),
},
Example {
example: "[2 0 1] | sort -r",
description: "sort the list by decreasing value",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(1), Value::test_int(0)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(1), Value::test_int(0)],
Span::test_data(),
)),
},
Example {
example: "[betty amy sarah] | sort",
description: "sort a list of strings",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("amy"),
Value::test_string("betty"),
Value::test_string("sarah"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
example: "[betty amy sarah] | sort -r",
description: "sort a list of strings in reverse",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("sarah"),
Value::test_string("betty"),
Value::test_string("amy"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Sort strings (case-insensitive)",
example: "[airplane Truck Car] | sort -i",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("airplane"),
Value::test_string("Car"),
Value::test_string("Truck"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Sort strings (reversed case-insensitive)",
example: "[airplane Truck Car] | sort -i -r",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_string("Truck"),
Value::test_string("Car"),
Value::test_string("airplane"),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Sort record by key (case-insensitive)",
@ -141,9 +141,10 @@ impl Command for Sort {
let natural = call.has_flag("natural");
let metadata = &input.metadata();
let span = input.span().unwrap_or(call.head);
match input {
// Records have two sorting methods, toggled by presence or absence of -v
PipelineData::Value(Value::Record { val, span }, ..) => {
PipelineData::Value(Value::Record { val, .. }, ..) => {
let sort_by_value = call.has_flag("values");
let record = sort_record(val, span, sort_by_value, reverse, insensitive, natural);
Ok(record.into_pipeline_data())
@ -257,20 +258,20 @@ pub fn sort(
}
_ => {
vec.sort_by(|a, b| {
let span_a = a.span();
let span_b = b.span();
if insensitive {
let lowercase_left = match a {
Value::String { val, span } => Value::String {
val: val.to_ascii_lowercase(),
span: *span,
},
Value::String { val, .. } => {
Value::string(val.to_ascii_lowercase(), span_a)
}
_ => a.clone(),
};
let lowercase_right = match b {
Value::String { val, span } => Value::String {
val: val.to_ascii_lowercase(),
span: *span,
},
Value::String { val, .. } => {
Value::string(val.to_ascii_lowercase(), span_b)
}
_ => b.clone(),
};
@ -311,30 +312,26 @@ pub fn process(
let left_res = match left_value {
Some(left_res) => left_res,
None => Value::Nothing { span },
None => Value::nothing(span),
};
let right_value = right.get_data_by_key(column);
let right_res = match right_value {
Some(right_res) => right_res,
None => Value::Nothing { span },
None => Value::nothing(span),
};
let result = if insensitive {
let span_left = left_res.span();
let span_right = right_res.span();
let lowercase_left = match left_res {
Value::String { val, span } => Value::String {
val: val.to_ascii_lowercase(),
span,
},
Value::String { val, .. } => Value::string(val.to_ascii_lowercase(), span_left),
_ => left_res,
};
let lowercase_right = match right_res {
Value::String { val, span } => Value::String {
val: val.to_ascii_lowercase(),
span,
},
Value::String { val, .. } => Value::string(val.to_ascii_lowercase(), span_right),
_ => right_res,
};
if natural {

View File

@ -58,8 +58,8 @@ impl Command for SortBy {
Example {
description: "Sort a table by a column (reversed order)",
example: "[[fruit count]; [apple 9] [pear 3] [orange 7]] | sort-by fruit -r",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["fruit".to_string(), "count".to_string()],
vals: vec![Value::test_string("pear"), Value::test_int(3)],
@ -73,8 +73,8 @@ impl Command for SortBy {
vals: vec![Value::test_string("apple"), Value::test_int(9)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -53,8 +53,8 @@ impl Command for SplitBy {
vals: vec![
Value::test_record(Record {
cols: vec!["2019".to_string()],
vals: vec![Value::List {
vals: vec![Value::test_record(Record {
vals: vec![Value::list(
vec![Value::test_record(Record {
cols: vec![
"name".to_string(),
"lang".to_string(),
@ -66,14 +66,14 @@ impl Command for SplitBy {
Value::test_string("2019"),
],
})],
span: Span::test_data(),
}],
Span::test_data(),
)],
}),
Value::test_record(Record {
cols: vec!["2019".to_string(), "2021".to_string()],
vals: vec![
Value::List {
vals: vec![Value::test_record(Record {
Value::list(
vec![Value::test_record(Record {
cols: vec![
"name".to_string(),
"lang".to_string(),
@ -85,10 +85,10 @@ impl Command for SplitBy {
Value::test_string("2019"),
],
})],
span: Span::test_data(),
},
Value::List {
vals: vec![Value::test_record(Record {
Span::test_data(),
),
Value::list(
vec![Value::test_record(Record {
cols: vec![
"name".to_string(),
"lang".to_string(),
@ -100,8 +100,8 @@ impl Command for SplitBy {
Value::test_string("2021"),
],
})],
span: Span::test_data(),
},
Span::test_data(),
),
],
}),
],
@ -215,20 +215,34 @@ pub fn data_split(
let mut splits = indexmap::IndexMap::new();
match value {
PipelineData::Value(Value::Record { val: grouped, span }, _) => {
for (idx, list) in grouped.vals.iter().enumerate() {
match data_group(list, splitter, span) {
Ok(grouped_vals) => {
if let Value::Record { val: sub, .. } = grouped_vals {
for (inner_idx, subset) in sub.vals.iter().enumerate() {
let s: &mut IndexMap<String, Value> =
splits.entry(sub.cols[inner_idx].clone()).or_default();
PipelineData::Value(v, _) => {
let span = v.span();
match v {
Value::Record { val: grouped, .. } => {
for (idx, list) in grouped.vals.iter().enumerate() {
match data_group(list, splitter, span) {
Ok(grouped_vals) => {
if let Value::Record { val: sub, .. } = grouped_vals {
for (inner_idx, subset) in sub.vals.iter().enumerate() {
let s: &mut IndexMap<String, Value> =
splits.entry(sub.cols[inner_idx].clone()).or_default();
s.insert(grouped.cols[idx].clone(), subset.clone());
s.insert(grouped.cols[idx].clone(), subset.clone());
}
}
}
Err(reason) => return Err(reason),
}
}
Err(reason) => return Err(reason),
}
_ => {
return Err(ShellError::GenericError(
"unsupported input".into(),
"requires a table with one row for splitting".into(),
Some(span),
None,
Vec::new(),
))
}
}
}

View File

@ -54,33 +54,33 @@ impl Command for Take {
let metadata = input.metadata();
match input {
PipelineData::Value(val, _) => match val {
Value::List { vals, .. } => Ok(vals
.into_iter()
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata)),
Value::Binary { val, span } => {
let slice: Vec<u8> = val.into_iter().take(rows_desired).collect();
Ok(PipelineData::Value(
Value::Binary { val: slice, span },
metadata,
))
PipelineData::Value(val, _) => {
let span = val.span();
match val {
Value::List { vals, .. } => Ok(vals
.into_iter()
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata)),
Value::Binary { val, .. } => {
let slice: Vec<u8> = val.into_iter().take(rows_desired).collect();
Ok(PipelineData::Value(Value::binary(slice, span), metadata))
}
Value::Range { val, .. } => Ok(val
.into_range_iter(ctrlc.clone())?
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata)),
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary or range".into(),
wrong_type: other.get_type().to_string(),
dst_span: call.head,
src_span: other.span(),
}),
}
Value::Range { val, .. } => Ok(val
.into_range_iter(ctrlc.clone())?
.take(rows_desired)
.into_pipeline_data(ctrlc)
.set_metadata(metadata)),
// Propagate errors by explicitly matching them before the final case.
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary or range".into(),
wrong_type: other.get_type().to_string(),
dst_span: call.head,
src_span: other.span(),
}),
},
}
PipelineData::ListStream(ls, metadata) => Ok(ls
.take(rows_desired)
.into_pipeline_data(ctrlc)
@ -107,24 +107,21 @@ impl Command for Take {
Example {
description: "Return the first item of a list/table",
example: "[1 2 3] | take 1",
result: Some(Value::List {
vals: vec![Value::test_int(1)],
span: Span::test_data(),
}),
result: Some(Value::list(vec![Value::test_int(1)], Span::test_data())),
},
Example {
description: "Return the first 2 items of a list/table",
example: "[1 2 3] | take 2",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(2)],
Span::test_data(),
)),
},
Example {
description: "Return the first two rows of a table",
example: "[[editions]; [2015] [2018] [2021]] | take 2",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["editions".to_string()],
vals: vec![Value::test_int(2015)],
@ -134,24 +131,21 @@ impl Command for Take {
vals: vec![Value::test_int(2018)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Return the first 2 bytes of a binary value",
example: "0x[01 23 45] | take 2",
result: Some(Value::Binary {
val: vec![0x01, 0x23],
span: Span::test_data(),
}),
result: Some(Value::binary(vec![0x01, 0x23], Span::test_data())),
},
Example {
description: "Return the first 3 elements of a range",
example: "1..10 | take 3",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
Span::test_data(),
)),
},
]
}

View File

@ -40,24 +40,24 @@ impl Command for TakeUntil {
Example {
description: "Take until the element is positive",
example: "[-1 -2 9 1] | take until {|x| $x > 0 }",
result: Some(Value::List {
vals: vec![Value::test_int(-1), Value::test_int(-2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(-1), Value::test_int(-2)],
Span::test_data(),
)),
},
Example {
description: "Take until the element is positive using stored condition",
example: "let cond = {|x| $x > 0 }; [-1 -2 9 1] | take until $cond",
result: Some(Value::List {
vals: vec![Value::test_int(-1), Value::test_int(-2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(-1), Value::test_int(-2)],
Span::test_data(),
)),
},
Example {
description: "Take until the field value is positive",
example: "[{a: -1} {a: -2} {a: 9} {a: 1}] | take until {|x| $x.a > 0 }",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_int(-1)],
@ -67,8 +67,8 @@ impl Command for TakeUntil {
vals: vec![Value::test_int(-2)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -40,24 +40,24 @@ impl Command for TakeWhile {
Example {
description: "Take while the element is negative",
example: "[-1 -2 9 1] | take while {|x| $x < 0 }",
result: Some(Value::List {
vals: vec![Value::test_int(-1), Value::test_int(-2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(-1), Value::test_int(-2)],
Span::test_data(),
)),
},
Example {
description: "Take while the element is negative using stored condition",
example: "let cond = {|x| $x < 0 }; [-1 -2 9 1] | take while $cond",
result: Some(Value::List {
vals: vec![Value::test_int(-1), Value::test_int(-2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(-1), Value::test_int(-2)],
Span::test_data(),
)),
},
Example {
description: "Take while the field value is negative",
example: "[{a: -1} {a: -2} {a: 9} {a: 1}] | take while {|x| $x.a < 0 }",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["a".to_string()],
vals: vec![Value::test_int(-1)],
@ -67,8 +67,8 @@ impl Command for TakeWhile {
vals: vec![Value::test_int(-2)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}

View File

@ -88,8 +88,8 @@ impl Command for Transpose {
Example {
description: "Transposes the table contents with default column names",
example: "[[c1 c2]; [1 2]] | transpose",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["column0".to_string(), "column1".to_string()],
vals: vec![Value::test_string("c1"), Value::test_int(1)],
@ -100,13 +100,13 @@ impl Command for Transpose {
}),
],
span,
}),
)),
},
Example {
description: "Transposes the table contents with specified column names",
example: "[[c1 c2]; [1 2]] | transpose key val",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["key".to_string(), "val".to_string()],
vals: vec![Value::test_string("c1"), Value::test_int(1)],
@ -117,14 +117,14 @@ impl Command for Transpose {
}),
],
span,
}),
)),
},
Example {
description:
"Transposes the table without column names and specify a new column name",
example: "[[c1 c2]; [1 2]] | transpose -i val",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["val".to_string()],
vals: vec![Value::test_int(1)],
@ -135,7 +135,7 @@ impl Command for Transpose {
}),
],
span,
}),
)),
},
Example {
description: "Transfer back to record with -d flag",
@ -260,19 +260,14 @@ pub fn transpose(
.iter()
.position(|y| y == &headers[column_num])
.expect("value is contained.");
let current_span = record.vals[index].span();
let new_val = match &record.vals[index] {
Value::List { vals, span } => {
Value::List { vals, .. } => {
let mut vals = vals.clone();
vals.push(x.clone());
Value::List {
vals: vals.to_vec(),
span: *span,
}
Value::list(vals.to_vec(), current_span)
}
v => Value::List {
vals: vec![v.clone(), x.clone()],
span: v.span(),
},
v => Value::list(vec![v.clone(), x.clone()], v.span()),
};
record.cols.remove(index);
record.vals.remove(index);
@ -298,19 +293,14 @@ pub fn transpose(
.iter()
.position(|y| y == &headers[column_num])
.expect("value is contained.");
let current_span = record.vals[index].span();
let new_val = match &record.vals[index] {
Value::List { vals, span } => {
Value::List { vals, .. } => {
let mut vals = vals.clone();
vals.push(Value::nothing(name));
Value::List {
vals: vals.to_vec(),
span: *span,
}
Value::list(vals.to_vec(), current_span)
}
v => Value::List {
vals: vec![v.clone(), Value::nothing(name)],
span: v.span(),
},
v => Value::list(vec![v.clone(), Value::nothing(name)], v.span()),
};
record.cols.remove(index);
record.vals.remove(index);

View File

@ -88,40 +88,40 @@ impl Command for Uniq {
Example {
description: "Return the distinct values of a list/table (remove duplicates so that each value occurs once only)",
example: "[2 3 3 4] | uniq",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
Span::test_data(),
)),
},
Example {
description: "Return the input values that occur more than once",
example: "[1 2 2] | uniq -d",
result: Some(Value::List {
vals: vec![Value::test_int(2)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2)],
Span::test_data(),
)),
},
Example {
description: "Return the input values that occur once only",
example: "[1 2 2] | uniq -u",
result: Some(Value::List {
vals: vec![Value::test_int(1)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1)],
Span::test_data(),
)),
},
Example {
description: "Ignore differences in case when comparing input values",
example: "['hello' 'goodbye' 'Hello'] | uniq -i",
result: Some(Value::List {
vals: vec![Value::test_string("hello"), Value::test_string("goodbye")],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_string("hello"), Value::test_string("goodbye")],
Span::test_data(),
)),
},
Example {
description: "Return a table containing the distinct input values together with their counts",
example: "[1 2 2] | uniq -c",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["value".to_string(), "count".to_string()],
vals: vec![Value::test_int(1), Value::test_int(1)],
@ -131,8 +131,8 @@ impl Command for Uniq {
vals: vec![Value::test_int(2), Value::test_int(2)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -185,29 +185,27 @@ impl ValueCounter {
}
fn clone_to_lowercase(value: &Value) -> Value {
let span = value.span();
match value {
Value::String { val: s, span } => Value::String {
val: s.clone().to_lowercase(),
span: *span,
},
Value::List { vals: vec, span } => Value::List {
vals: vec.iter().map(clone_to_lowercase).collect(),
span: *span,
},
Value::Record { val: record, span } => Value::record(
Value::String { val: s, .. } => Value::string(s.clone().to_lowercase(), span),
Value::List { vals: vec, .. } => {
Value::list(vec.iter().map(clone_to_lowercase).collect(), span)
}
Value::Record { val: record, .. } => Value::record(
Record {
cols: record.cols.clone(),
vals: record.vals.iter().map(clone_to_lowercase).collect(),
},
*span,
span,
),
other => other.clone(),
}
}
fn sort_attributes(val: Value) -> Value {
let span = val.span();
match val {
Value::Record { val, span } => {
Value::Record { val, .. } => {
let sorted = val
.into_iter()
.sorted_by(|a, b| a.0.cmp(&b.0))
@ -227,10 +225,9 @@ fn sort_attributes(val: Value) -> Value {
span,
)
}
Value::List { vals, span } => Value::List {
vals: vals.into_iter().map(sort_attributes).collect_vec(),
span,
},
Value::List { vals, .. } => {
Value::list(vals.into_iter().map(sort_attributes).collect_vec(), span)
}
other => other,
}
}
@ -321,12 +318,9 @@ pub fn uniq(
uniq_values.into_iter().map(|v| v.val).collect()
};
Ok(Value::List {
vals: result,
span: head,
}
.into_pipeline_data()
.set_metadata(metadata))
Ok(Value::list(result, head)
.into_pipeline_data()
.set_metadata(metadata))
}
fn sort(iter: IntoIter<String, ValueCounter>) -> Vec<ValueCounter> {

View File

@ -92,8 +92,8 @@ impl Command for UniqBy {
vec![Example {
description: "Get rows from table filtered by column uniqueness ",
example: "[[fruit count]; [apple 9] [apple 2] [pear 3] [orange 7]] | uniq-by fruit",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["fruit".to_string(), "count".to_string()],
vals: vec![Value::test_string("apple"), Value::test_int(9)],
@ -107,35 +107,35 @@ impl Command for UniqBy {
vals: vec![Value::test_string("orange"), Value::test_int(7)],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}
}
fn validate(vec: Vec<Value>, columns: &Vec<String>, span: Span) -> Result<(), ShellError> {
if let Some(Value::Record {
val: record,
span: val_span,
}) = vec.first()
{
if columns.is_empty() {
// This uses the same format as the 'requires a column name' error in split_by.rs
return Err(ShellError::GenericError(
"expected name".into(),
"requires a column name to filter table data".into(),
Some(span),
None,
Vec::new(),
));
}
let first = vec.first();
if let Some(v) = first {
let val_span = v.span();
if let Value::Record { val: record, .. } = &v {
if columns.is_empty() {
// This uses the same format as the 'requires a column name' error in split_by.rs
return Err(ShellError::GenericError(
"expected name".into(),
"requires a column name to filter table data".into(),
Some(span),
None,
Vec::new(),
));
}
if let Some(nonexistent) = nonexistent_column(columns.clone(), record.cols.clone()) {
return Err(ShellError::CantFindColumn {
col_name: nonexistent,
span,
src_span: *val_span,
});
if let Some(nonexistent) = nonexistent_column(columns.clone(), record.cols.clone()) {
return Err(ShellError::CantFindColumn {
col_name: nonexistent,
span,
src_span: val_span,
});
}
}
}
@ -155,10 +155,7 @@ fn item_mapper_by_col(cols: Vec<String>) -> impl Fn(crate::ItemMapperState) -> c
Box::new(move |ms: crate::ItemMapperState| -> crate::ValueCounter {
let item_column_values = get_data_by_columns(&columns, &ms.item);
let col_vals = Value::List {
vals: item_column_values,
span: Span::unknown(),
};
let col_vals = Value::list(item_column_values, Span::unknown());
crate::ValueCounter::new_vals_to_compare(ms.item, ms.flag_ignore_case, col_vals, ms.index)
})

View File

@ -65,35 +65,35 @@ impl Command for Update {
Example {
description: "Use in closure form for more involved updating logic",
example: "[[count fruit]; [1 'apple']] | enumerate | update item.count {|e| ($e.item.fruit | str length) + $e.index } | get item",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["count".into(), "fruit".into()],
vals: vec![Value::test_int(5), Value::test_string("apple")],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Alter each value in the 'authors' column to use a single string instead of a list",
example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | update authors {|row| $row.authors | str join ','}",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["project".into(), "authors".into()],
vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "You can also use a simple command to update 'authors' to a single string",
example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | update authors {|| str join ','}",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["project".into(), "authors".into()],
vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
}
]
}
@ -140,12 +140,7 @@ fn update(
let input_at_path = match input.clone().follow_cell_path(&cell_path.members, false)
{
Err(e) => {
return Value::Error {
error: Box::new(e),
span,
}
}
Err(e) => return Value::error(e, span),
Ok(v) => v,
};
let output = eval_block(
@ -162,18 +157,12 @@ fn update(
if let Err(e) =
input.update_data_at_cell_path(&cell_path.members, pd.into_value(span))
{
return Value::Error {
error: Box::new(e),
span,
};
return Value::error(e, span);
}
input
}
Err(e) => Value::Error {
error: Box::new(e),
span,
},
Err(e) => Value::error(e, span),
}
},
ctrlc,
@ -210,10 +199,7 @@ fn update(
let replacement = replacement.clone();
if let Err(e) = input.update_data_at_cell_path(&cell_path.members, replacement) {
return Value::Error {
error: Box::new(e),
span,
};
return Value::error(e, span);
}
input

View File

@ -68,8 +68,8 @@ impl Command for Upsert {
Example {
description: "Update each row of a table",
example: "[[name lang]; [Nushell ''] [Reedline '']] | upsert lang 'Rust'",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: vec!["name".into(), "lang".into()],
vals: vec![Value::test_string("Nushell"), Value::test_string("Rust")],
@ -79,8 +79,8 @@ impl Command for Upsert {
vals: vec![Value::test_string("Reedline"), Value::test_string("Rust")],
}),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Insert a new entry into a single record",
@ -92,34 +92,34 @@ impl Command for Upsert {
}, Example {
description: "Use in closure form for more involved updating logic",
example: "[[count fruit]; [1 'apple']] | enumerate | upsert item.count {|e| ($e.item.fruit | str length) + $e.index } | get item",
result: Some(Value::List {
vals: vec![Value::test_record(Record {
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["count".into(), "fruit".into()],
vals: vec![Value::test_int(5), Value::test_string("apple")],
})],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Upsert an int into a list, updating an existing value based on the index",
example: "[1 2 3] | upsert 0 2",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(2), Value::test_int(3)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(2), Value::test_int(3)],
Span::test_data(),
)),
},
Example {
description: "Upsert an int into a list, inserting a new value based on the index",
example: "[1 2 3] | upsert 3 4",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
Value::test_int(4),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -178,18 +178,12 @@ fn upsert(
if let Err(e) =
input.upsert_data_at_cell_path(&cell_path.members, pd.into_value(span))
{
return Value::Error {
error: Box::new(e),
span,
};
return Value::error(e, span);
}
input
}
Err(e) => Value::Error {
error: Box::new(e),
span,
},
Err(e) => Value::error(e, span),
}
},
ctrlc,
@ -225,10 +219,7 @@ fn upsert(
let replacement = replacement.clone();
if let Err(e) = input.upsert_data_at_cell_path(&cell_path.members, replacement) {
return Value::Error {
error: Box::new(e),
span,
};
return Value::error(e, span);
}
input

View File

@ -62,19 +62,11 @@ pub fn boolean_fold(
}
Ok(pipeline_data) => {
if pipeline_data.into_value(span).is_true() == accumulator {
return Ok(Value::Bool {
val: accumulator,
span,
}
.into_pipeline_data());
return Ok(Value::bool(accumulator, span).into_pipeline_data());
}
}
}
}
Ok(Value::Bool {
val: !accumulator,
span,
}
.into_pipeline_data())
Ok(Value::bool(!accumulator, span).into_pipeline_data())
}

Some files were not shown because too many files have changed in this diff Show More