2019-12-17 19:54:39 +01:00
|
|
|
|
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
|
|
|
|
|
use nu_test_support::playground::Playground;
|
|
|
|
|
use nu_test_support::{nu, pipeline};
|
2019-12-15 17:15:06 +01:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn table_to_csv_text_and_from_csv_text_back_into_table() {
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: "tests/fixtures/formats",
|
2022-09-29 00:08:17 +02:00
|
|
|
|
"open caco3_plastics.csv | to csv | from csv | first | get origin "
|
2019-12-15 17:15:06 +01:00
|
|
|
|
);
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert_eq!(actual.out, "SPAIN");
|
2019-12-15 17:15:06 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn table_to_csv_text() {
|
|
|
|
|
Playground::setup("filter_to_csv_test_1", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"csv_text_sample.txt",
|
|
|
|
|
r#"
|
|
|
|
|
importer,shipper,tariff_item,name,origin
|
|
|
|
|
Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain
|
|
|
|
|
Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open csv_text_sample.txt
|
|
|
|
|
| lines
|
2020-09-16 21:59:32 +02:00
|
|
|
|
| str trim
|
2020-05-24 08:41:30 +02:00
|
|
|
|
| split column "," a b c d origin
|
2019-12-15 17:15:06 +01:00
|
|
|
|
| last 1
|
2020-05-04 10:44:33 +02:00
|
|
|
|
| to csv
|
2019-12-15 17:15:06 +01:00
|
|
|
|
| lines
|
2022-02-04 03:01:45 +01:00
|
|
|
|
| get 1
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert!(actual
|
|
|
|
|
.out
|
|
|
|
|
.contains("Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia"));
|
2019-12-15 17:15:06 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn table_to_csv_text_skipping_headers_after_conversion() {
|
|
|
|
|
Playground::setup("filter_to_csv_test_2", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"csv_text_sample.txt",
|
|
|
|
|
r#"
|
|
|
|
|
importer,shipper,tariff_item,name,origin
|
|
|
|
|
Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain
|
|
|
|
|
Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open csv_text_sample.txt
|
|
|
|
|
| lines
|
2020-09-16 21:59:32 +02:00
|
|
|
|
| str trim
|
2020-05-24 08:41:30 +02:00
|
|
|
|
| split column "," a b c d origin
|
2019-12-15 17:15:06 +01:00
|
|
|
|
| last 1
|
2021-02-22 08:25:17 +01:00
|
|
|
|
| to csv --noheaders
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert!(actual
|
|
|
|
|
.out
|
|
|
|
|
.contains("Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia"));
|
2019-12-15 17:15:06 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2020-03-16 21:50:45 +01:00
|
|
|
|
fn infers_types() {
|
2019-12-15 17:15:06 +01:00
|
|
|
|
Playground::setup("filter_from_csv_test_1", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2020-03-16 21:50:45 +01:00
|
|
|
|
"los_cuatro_mosqueteros.csv",
|
|
|
|
|
r#"
|
2020-04-06 09:16:14 +02:00
|
|
|
|
first_name,last_name,rusty_luck,d
|
2020-03-16 21:50:45 +01:00
|
|
|
|
Andrés,Robalino,1,d
|
2023-03-15 06:54:55 +01:00
|
|
|
|
JT,Turner,1,d
|
2020-03-16 21:50:45 +01:00
|
|
|
|
Yehuda,Katz,1,d
|
|
|
|
|
Jason,Gedge,1,d
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_cuatro_mosqueteros.csv
|
|
|
|
|
| where rusty_luck > 0
|
2021-03-13 22:46:40 +01:00
|
|
|
|
| length
|
2020-03-16 21:50:45 +01:00
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert_eq!(actual.out, "4");
|
2020-03-16 21:50:45 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_to_table() {
|
|
|
|
|
Playground::setup("filter_from_csv_test_2", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name,last_name,rusty_luck
|
|
|
|
|
Andrés,Robalino,1
|
2023-03-15 06:54:55 +01:00
|
|
|
|
JT,Turner,1
|
2019-12-15 17:15:06 +01:00
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
2020-05-04 10:44:33 +02:00
|
|
|
|
| from csv
|
2019-12-15 17:15:06 +01:00
|
|
|
|
| get rusty_luck
|
2021-03-13 22:46:40 +01:00
|
|
|
|
| length
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert_eq!(actual.out, "3");
|
2019-12-15 17:15:06 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_with_separator_to_table() {
|
2020-03-16 21:50:45 +01:00
|
|
|
|
Playground::setup("filter_from_csv_test_3", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name;last_name;rusty_luck
|
|
|
|
|
Andrés;Robalino;1
|
2023-03-15 06:54:55 +01:00
|
|
|
|
JT;Turner;1
|
2019-12-15 17:15:06 +01:00
|
|
|
|
Yehuda;Katz;1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
2022-02-04 03:01:45 +01:00
|
|
|
|
| from csv --separator ";"
|
2019-12-15 17:15:06 +01:00
|
|
|
|
| get rusty_luck
|
2021-03-13 22:46:40 +01:00
|
|
|
|
| length
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert_eq!(actual.out, "3");
|
2019-12-15 17:15:06 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_with_tab_separator_to_table() {
|
2020-03-16 21:50:45 +01:00
|
|
|
|
Playground::setup("filter_from_csv_test_4", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name last_name rusty_luck
|
|
|
|
|
Andrés Robalino 1
|
2023-03-15 06:54:55 +01:00
|
|
|
|
JT Turner 1
|
2019-12-15 17:15:06 +01:00
|
|
|
|
Yehuda Katz 1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
2022-02-04 03:01:45 +01:00
|
|
|
|
| from csv --separator (char tab)
|
2019-12-15 17:15:06 +01:00
|
|
|
|
| get rusty_luck
|
2021-03-13 22:46:40 +01:00
|
|
|
|
| length
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert_eq!(actual.out, "3");
|
2019-12-15 17:15:06 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
Replace `ExternalStream` with new `ByteStream` type (#12774)
# Description
This PR introduces a `ByteStream` type which is a `Read`-able stream of
bytes. Internally, it has an enum over three different byte stream
sources:
```rust
pub enum ByteStreamSource {
Read(Box<dyn Read + Send + 'static>),
File(File),
Child(ChildProcess),
}
```
This is in comparison to the current `RawStream` type, which is an
`Iterator<Item = Vec<u8>>` and has to allocate for each read chunk.
Currently, `PipelineData::ExternalStream` serves a weird dual role where
it is either external command output or a wrapper around `RawStream`.
`ByteStream` makes this distinction more clear (via `ByteStreamSource`)
and replaces `PipelineData::ExternalStream` in this PR:
```rust
pub enum PipelineData {
Empty,
Value(Value, Option<PipelineMetadata>),
ListStream(ListStream, Option<PipelineMetadata>),
ByteStream(ByteStream, Option<PipelineMetadata>),
}
```
The PR is relatively large, but a decent amount of it is just repetitive
changes.
This PR fixes #7017, fixes #10763, and fixes #12369.
This PR also improves performance when piping external commands. Nushell
should, in most cases, have competitive pipeline throughput compared to,
e.g., bash.
| Command | Before (MB/s) | After (MB/s) | Bash (MB/s) |
| -------------------------------------------------- | -------------:|
------------:| -----------:|
| `throughput \| rg 'x'` | 3059 | 3744 | 3739 |
| `throughput \| nu --testbin relay o> /dev/null` | 3508 | 8087 | 8136 |
# User-Facing Changes
- This is a breaking change for the plugin communication protocol,
because the `ExternalStreamInfo` was replaced with `ByteStreamInfo`.
Plugins now only have to deal with a single input stream, as opposed to
the previous three streams: stdout, stderr, and exit code.
- The output of `describe` has been changed for external/byte streams.
- Temporary breaking change: `bytes starts-with` no longer works with
byte streams. This is to keep the PR smaller, and `bytes ends-with`
already does not work on byte streams.
- If a process core dumped, then instead of having a `Value::Error` in
the `exit_code` column of the output returned from `complete`, it now is
a `Value::Int` with the negation of the signal number.
# After Submitting
- Update docs and book as necessary
- Release notes (e.g., plugin protocol changes)
- Adapt/convert commands to work with byte streams (high priority is
`str length`, `bytes starts-with`, and maybe `bytes ends-with`).
- Refactor the `tee` code, Devyn has already done some work on this.
---------
Co-authored-by: Devyn Cairns <devyn.cairns@gmail.com>
2024-05-16 16:11:18 +02:00
|
|
|
|
#[ignore = "csv crate has a bug when the last line is a comment: https://github.com/BurntSushi/rust-csv/issues/363"]
|
2023-03-16 23:49:46 +01:00
|
|
|
|
fn from_csv_text_with_comments_to_table() {
|
2020-03-16 21:50:45 +01:00
|
|
|
|
Playground::setup("filter_from_csv_test_5", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-03-16 23:49:46 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
# This is a comment
|
|
|
|
|
first_name,last_name,rusty_luck
|
|
|
|
|
# This one too
|
|
|
|
|
Andrés,Robalino,1
|
|
|
|
|
Jonathan,Turner,1
|
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
# This one also
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r##"
|
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --comment "#"
|
|
|
|
|
| get rusty_luck
|
|
|
|
|
| length
|
|
|
|
|
"##
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert_eq!(actual.out, "3");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_with_custom_quotes_to_table() {
|
|
|
|
|
Playground::setup("filter_from_csv_test_6", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-03-16 23:49:46 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name,last_name,rusty_luck
|
|
|
|
|
'And''rés',Robalino,1
|
|
|
|
|
Jonathan,Turner,1
|
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --quote "'"
|
|
|
|
|
| first
|
|
|
|
|
| get first_name
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert_eq!(actual.out, "And'rés");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_with_custom_escapes_to_table() {
|
|
|
|
|
Playground::setup("filter_from_csv_test_7", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-03-16 23:49:46 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name,last_name,rusty_luck
|
|
|
|
|
"And\"rés",Robalino,1
|
|
|
|
|
Jonathan,Turner,1
|
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
2023-09-01 00:08:27 +02:00
|
|
|
|
r"
|
2023-03-16 23:49:46 +01:00
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --escape '\'
|
|
|
|
|
| first
|
|
|
|
|
| get first_name
|
2023-09-01 00:08:27 +02:00
|
|
|
|
"
|
2023-03-16 23:49:46 +01:00
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert_eq!(actual.out, "And\"rés");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_skipping_headers_to_table() {
|
|
|
|
|
Playground::setup("filter_from_csv_test_8", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"los_tres_amigos.txt",
|
|
|
|
|
r#"
|
|
|
|
|
Andrés,Robalino,1
|
2023-03-15 06:54:55 +01:00
|
|
|
|
JT,Turner,1
|
2019-12-15 17:15:06 +01:00
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_amigos.txt
|
2021-02-22 08:25:17 +01:00
|
|
|
|
| from csv --noheaders
|
2022-02-20 01:26:47 +01:00
|
|
|
|
| get column3
|
2021-03-13 22:46:40 +01:00
|
|
|
|
| length
|
2019-12-15 17:15:06 +01:00
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2020-05-07 13:03:43 +02:00
|
|
|
|
assert_eq!(actual.out, "3");
|
2019-12-15 17:15:06 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
2023-01-25 15:42:53 +01:00
|
|
|
|
|
2023-03-16 23:49:46 +01:00
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_with_missing_columns_to_table() {
|
|
|
|
|
Playground::setup("filter_from_csv_test_9", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-03-16 23:49:46 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name,last_name,rusty_luck
|
|
|
|
|
Andrés,Robalino
|
|
|
|
|
Jonathan,Turner,1
|
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --flexible
|
|
|
|
|
| get -i rusty_luck
|
|
|
|
|
| compact
|
|
|
|
|
| length
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert_eq!(actual.out, "2");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_with_multiple_char_separator() {
|
|
|
|
|
Playground::setup("filter_from_csv_test_10", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-03-16 23:49:46 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name,last_name,rusty_luck
|
|
|
|
|
Andrés,Robalino,1
|
|
|
|
|
Jonathan,Turner,1
|
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --separator "li"
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2023-08-31 18:55:39 +02:00
|
|
|
|
assert!(actual
|
|
|
|
|
.err
|
|
|
|
|
.contains("separator should be a single char or a 4-byte unicode"));
|
2023-03-16 23:49:46 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_text_with_wrong_type_separator() {
|
|
|
|
|
Playground::setup("filter_from_csv_test_11", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-03-16 23:49:46 +01:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name,last_name,rusty_luck
|
|
|
|
|
Andrés,Robalino,1
|
|
|
|
|
Jonathan,Turner,1
|
|
|
|
|
Yehuda,Katz,1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --separator ('123' | into int)
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2023-08-31 18:55:39 +02:00
|
|
|
|
assert!(actual.err.contains("can't convert int to string"));
|
2023-03-16 23:49:46 +01:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-25 15:42:53 +01:00
|
|
|
|
#[test]
|
|
|
|
|
fn table_with_record_error() {
|
2023-07-17 18:43:51 +02:00
|
|
|
|
let actual = nu!(pipeline(
|
2023-01-25 15:42:53 +01:00
|
|
|
|
r#"
|
2024-03-11 19:46:04 +01:00
|
|
|
|
[[a b]; [1 2] [3 {a: 1 b: 2}]]
|
2023-01-25 15:42:53 +01:00
|
|
|
|
| to csv
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert!(actual.err.contains("can't convert"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn list_not_table_error() {
|
2023-07-17 18:43:51 +02:00
|
|
|
|
let actual = nu!(pipeline(
|
2023-01-25 15:42:53 +01:00
|
|
|
|
r#"
|
|
|
|
|
[{a: 1 b: 2} {a: 3 b: 4} 1]
|
|
|
|
|
| to csv
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert!(actual.err.contains("can't convert"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn string_to_csv_error() {
|
2023-07-17 18:43:51 +02:00
|
|
|
|
let actual = nu!(pipeline(
|
2023-01-25 15:42:53 +01:00
|
|
|
|
r#"
|
|
|
|
|
'qwe' | to csv
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
2023-07-14 05:20:35 +02:00
|
|
|
|
assert!(actual.err.contains("command doesn't support"))
|
2023-01-25 15:42:53 +01:00
|
|
|
|
}
|
2023-08-31 18:55:39 +02:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_csv_with_unicode_sep() {
|
|
|
|
|
Playground::setup("filter_from_csv_unicode_sep_test_3", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-08-31 18:55:39 +02:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_name;last_name;rusty_luck
|
|
|
|
|
Andrés;Robalino;1
|
|
|
|
|
JT;Turner;1
|
|
|
|
|
Yehuda;Katz;1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --separator "003B"
|
|
|
|
|
| get rusty_luck
|
|
|
|
|
| length
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert_eq!(actual.out, "3");
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parses_csv_with_unicode_x1f_sep() {
|
|
|
|
|
Playground::setup("filter_from_csv_unicode_sep_x1f_test_3", |dirs, sandbox| {
|
2024-05-04 02:53:15 +02:00
|
|
|
|
sandbox.with_files(&[FileWithContentToBeTrimmed(
|
2023-08-31 18:55:39 +02:00
|
|
|
|
"los_tres_caballeros.txt",
|
|
|
|
|
r#"
|
|
|
|
|
first_namelast_namerusty_luck
|
|
|
|
|
AndrésRobalino1
|
|
|
|
|
JTTurner1
|
|
|
|
|
YehudaKatz1
|
|
|
|
|
"#,
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
let actual = nu!(
|
|
|
|
|
cwd: dirs.test(), pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
open los_tres_caballeros.txt
|
|
|
|
|
| from csv --separator "001F"
|
|
|
|
|
| get rusty_luck
|
|
|
|
|
| length
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert_eq!(actual.out, "3");
|
|
|
|
|
})
|
|
|
|
|
}
|
fix: Ensure consistent vals and cols when parsing with `--flexible` (#10814)
# Description
`from tsv` and `from csv` both support a `--flexible` flag. This flag
can be used to "allow the number of fields in records to be variable".
Previously, a record's invariant that `rec.cols.len() == rec.vals.len()`
could be broken during parsing. This can cause runtime errors as in
#10693. Other commands, like `select` were also affected.
The inconsistencies are somewhat hard to see, as most nushell code
assumes an equal number of columns and values.
# Before
### Fewer values than columns
```nushell
> let record = (echo "one,two\n1" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# But only one value
> $record | values | to nuon
[1]
# And printing the record doesn't show the second column!
> $record | to nuon
{one: 1}
```
### More values than columns
```nushell
> let record = (echo "one,two\n1,2,3" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# But three values
> $record | values | to nuon
[1, 2, 3]
# And printing the record doesn't show the third value!
> $record | to nuon
{one: 1, two: 2}
```
# After
### Fewer values than columns
```nushell
> let record = (echo "one,two\n1" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# And a matching number of values
> $record | values | to nuon
[1, null]
# And printing the record works as expected
> $record | to nuon
{one: 1, two: null}
```
### More values than columns
```nushell
> let record = (echo "one,two\n1,2,3" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# And a matching number of values
> $record | values | to nuon
[1, 2]
# And printing the record works as expected
> $record | to nuon
{one: 1, two: 2}
```
# User-Facing Changes
Using the `--flexible` flag with `from csv` and `from tsv` will not
result in corrupted record state.
# 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.
-->
2023-10-24 22:54:26 +02:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_test_flexible_extra_vals() {
|
|
|
|
|
let actual = nu!(pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
echo "a,b\n1,2,3" | from csv --flexible | first | values | to nuon
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
assert_eq!(actual.out, "[1, 2]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn from_csv_test_flexible_missing_vals() {
|
|
|
|
|
let actual = nu!(pipeline(
|
|
|
|
|
r#"
|
|
|
|
|
echo "a,b\n1" | from csv --flexible | first | values | to nuon
|
|
|
|
|
"#
|
|
|
|
|
));
|
|
|
|
|
assert_eq!(actual.out, "[1, null]");
|
|
|
|
|
}
|