From 6b4d06d8a7ab02501433acfa21cc10a58177607c Mon Sep 17 00:00:00 2001 From: panicbit Date: Sun, 6 Aug 2023 13:17:03 +0200 Subject: [PATCH] do not emit None mid-stream during parse (#9925) # Description Currently `parse` acts like a `.filter` over an iterator, except that it emits `None` for elements that can't be parsed. This causes consumers of the adapted iterator to stop iterating too early. The correct behaviour is to keep pulling the inner iterator until either the end of it is reached or an element can be parsed. - this PR should close #9906 # User-Facing Changes List streams won't be truncated anymore after the first parse failure. # Tests + Formatting - [x] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style - [x] `cargo test --workspace` to check that all tests pass - 11 tests fail, but the same 11 tests fail on main as well - [x] `cargo run -- -c "use std testing; testing run-tests --path crates/nu-std"` to run the tests for the standard library # After Submitting --- crates/nu-command/src/strings/parse.rs | 46 +++++++++++++++-------- crates/nu-command/tests/commands/parse.rs | 14 +++++++ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/crates/nu-command/src/strings/parse.rs b/crates/nu-command/src/strings/parse.rs index f74fd42371..4a80119e03 100644 --- a/crates/nu-command/src/strings/parse.rs +++ b/crates/nu-command/src/strings/parse.rs @@ -1,3 +1,6 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + use fancy_regex::Regex; use nu_engine::CallExt; use nu_protocol::ast::Call; @@ -230,6 +233,7 @@ fn operate( regex: regex_pattern, columns, stream: stream.stream, + ctrlc: ctrlc.clone(), }, ctrlc, ), @@ -329,6 +333,7 @@ pub struct ParseStreamer { regex: Regex, columns: Vec, stream: Box + Send + 'static>, + ctrlc: Option>, } impl Iterator for ParseStreamer { @@ -338,27 +343,38 @@ impl Iterator for ParseStreamer { return Some(self.excess.remove(0)); } - let v = self.stream.next(); + loop { + if let Some(ctrlc) = &self.ctrlc { + if ctrlc.load(Ordering::SeqCst) { + break None; + } + } - if let Some(v) = v { - match v.as_string() { - Ok(s) => stream_helper( - self.regex.clone(), - v.span().unwrap_or(self.span), - s, - self.columns.clone(), - &mut self.excess, - ), - Err(_) => Some(Value::Error { + let Some(v) = self.stream.next() else { return None }; + + let Ok(s) = v.as_string() else { + return Some(Value::Error { error: Box::new(ShellError::PipelineMismatch { exp_input_type: "string".into(), dst_span: self.span, src_span: v.span().unwrap_or(self.span), }), - }), - } - } else { - None + }) + }; + + let parsed = stream_helper( + self.regex.clone(), + v.span().unwrap_or(self.span), + s, + self.columns.clone(), + &mut self.excess, + ); + + if parsed.is_none() { + continue; + }; + + return parsed; } } } diff --git a/crates/nu-command/tests/commands/parse.rs b/crates/nu-command/tests/commands/parse.rs index b00641d2d8..ec81b3fe36 100644 --- a/crates/nu-command/tests/commands/parse.rs +++ b/crates/nu-command/tests/commands/parse.rs @@ -197,4 +197,18 @@ mod regex { assert_eq!(actual.out, "table (stream)") } + + #[test] + fn parse_does_not_truncate_list_streams() { + let actual = nu!(pipeline( + r#" + [a b c] + | each {|x| $x} + | parse --regex "[ac]" + | length + "# + )); + + assert_eq!(actual.out, "2"); + } }