From 4858a9a81787df53f9f3840f04abfd36c94599eb Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Wed, 31 Aug 2022 18:09:40 -0500 Subject: [PATCH] Revert "Add support for optional list stream output formatting (#6325)" (#6454) This reverts commit ec4e3a6d5c1ee02cf027bec9bb0da6ffd18e45bf. --- crates/nu-cli/src/eval_file.rs | 2 +- crates/nu-cli/src/util.rs | 2 +- crates/nu-command/src/core_commands/echo.rs | 44 ++++++------ crates/nu-command/src/core_commands/for_.rs | 2 +- crates/nu-command/src/filters/columns.rs | 2 +- crates/nu-command/src/filters/drop/column.rs | 2 +- crates/nu-command/src/filters/find.rs | 2 +- crates/nu-command/src/filters/length.rs | 2 +- crates/nu-command/src/filters/lines.rs | 2 +- crates/nu-command/src/filters/par_each.rs | 2 +- crates/nu-command/src/filters/reject.rs | 2 +- crates/nu-command/src/filters/select.rs | 2 +- crates/nu-command/src/filters/wrap.rs | 2 +- crates/nu-command/src/math/utils.rs | 4 +- crates/nu-command/src/system/complete.rs | 2 +- crates/nu-command/src/viewers/griddle.rs | 2 +- crates/nu-command/src/viewers/table.rs | 75 +------------------- crates/nu-engine/src/eval.rs | 7 +- crates/nu-protocol/src/pipeline_data.rs | 63 ++++------------ crates/nu-protocol/src/value/mod.rs | 8 +-- crates/nu-protocol/src/value/stream.rs | 12 ++-- 21 files changed, 60 insertions(+), 181 deletions(-) diff --git a/crates/nu-cli/src/eval_file.rs b/crates/nu-cli/src/eval_file.rs index 9b1c45f55..c1a481bbe 100644 --- a/crates/nu-cli/src/eval_file.rs +++ b/crates/nu-cli/src/eval_file.rs @@ -109,7 +109,7 @@ pub fn print_table_or_error( // Make sure everything has finished if let Some(exit_code) = exit_code { - let mut exit_code: Vec<_> = exit_code.into_iter().map(|(value, _)| value).collect(); + let mut exit_code: Vec<_> = exit_code.into_iter().collect(); exit_code .pop() .and_then(|last_exit_code| match last_exit_code { diff --git a/crates/nu-cli/src/util.rs b/crates/nu-cli/src/util.rs index 664b81afc..75cdfa3dc 100644 --- a/crates/nu-cli/src/util.rs +++ b/crates/nu-cli/src/util.rs @@ -233,7 +233,7 @@ pub fn eval_source( match eval_block(engine_state, stack, &block, input, false, false) { Ok(mut pipeline_data) => { if let PipelineData::ExternalStream { exit_code, .. } = &mut pipeline_data { - if let Some((exit_code, _)) = exit_code.take().and_then(|it| it.last()) { + if let Some(exit_code) = exit_code.take().and_then(|it| it.last()) { stack.add_env_var("LAST_EXIT_CODE".to_string(), exit_code); } else { set_last_exit_code(stack, 0); diff --git a/crates/nu-command/src/core_commands/echo.rs b/crates/nu-command/src/core_commands/echo.rs index aa1ba796e..0f74310db 100644 --- a/crates/nu-command/src/core_commands/echo.rs +++ b/crates/nu-command/src/core_commands/echo.rs @@ -34,32 +34,28 @@ impl Command for Echo { call: &Call, _input: PipelineData, ) -> Result { - call.rest(engine_state, stack, 0) - .map(|to_be_echoed: Vec| { - let n = to_be_echoed.len(); - match n.cmp(&1usize) { - // More than one value is converted in a stream of values - std::cmp::Ordering::Greater => PipelineData::ListStream( - ListStream::from_stream( - to_be_echoed.into_iter(), - engine_state.ctrlc.clone(), - ), - None, - ), + call.rest(engine_state, stack, 0).map(|to_be_echoed| { + let n = to_be_echoed.len(); + match n.cmp(&1usize) { + // More than one value is converted in a stream of values + std::cmp::Ordering::Greater => PipelineData::ListStream( + ListStream::from_stream(to_be_echoed.into_iter(), engine_state.ctrlc.clone()), + None, + ), - // But a single value can be forwarded as it is - std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None), + // But a single value can be forwarded as it is + std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None), - // When there are no elements, we echo the empty string - std::cmp::Ordering::Less => PipelineData::Value( - Value::String { - val: "".to_string(), - span: call.head, - }, - None, - ), - } - }) + // When there are no elements, we echo the empty string + std::cmp::Ordering::Less => PipelineData::Value( + Value::String { + val: "".to_string(), + span: call.head, + }, + None, + ), + } + }) } fn examples(&self) -> Vec { diff --git a/crates/nu-command/src/core_commands/for_.rs b/crates/nu-command/src/core_commands/for_.rs index 7297ced2c..58d4394a5 100644 --- a/crates/nu-command/src/core_commands/for_.rs +++ b/crates/nu-command/src/core_commands/for_.rs @@ -91,7 +91,7 @@ impl Command for For { Value::List { vals, .. } => { Ok(ListStream::from_stream(vals.into_iter(), ctrlc.clone()) .enumerate() - .map(move |(idx, (x, _))| { + .map(move |(idx, x)| { stack.with_env(&orig_env_vars, &orig_env_hidden); stack.add_var( diff --git a/crates/nu-command/src/filters/columns.rs b/crates/nu-command/src/filters/columns.rs index f06b351de..840b1e055 100644 --- a/crates/nu-command/src/filters/columns.rs +++ b/crates/nu-command/src/filters/columns.rs @@ -91,7 +91,7 @@ fn getcol( .into_pipeline_data(engine_state.ctrlc.clone())) } PipelineData::ListStream(stream, ..) => { - let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect(); + let v: Vec<_> = stream.into_iter().collect(); let input_cols = get_columns(&v); Ok(input_cols diff --git a/crates/nu-command/src/filters/drop/column.rs b/crates/nu-command/src/filters/drop/column.rs index 6d917d50d..68db65579 100644 --- a/crates/nu-command/src/filters/drop/column.rs +++ b/crates/nu-command/src/filters/drop/column.rs @@ -122,7 +122,7 @@ fn dropcol( PipelineData::ListStream(stream, ..) => { let mut output = vec![]; - let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect(); + let v: Vec<_> = stream.into_iter().collect(); let input_cols = get_input_cols(v.clone()); let kc = get_keep_columns(input_cols, columns); keep_columns = get_cellpath_columns(kc, span); diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index 065db85fa..3fe81b121 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -364,7 +364,7 @@ fn find_with_rest_and_highlight( PipelineData::ListStream(stream, meta) => { Ok(ListStream::from_stream( stream - .map(move |(mut x, _)| match &mut x { + .map(move |mut x| match &mut x { Value::Record { cols, vals, span } => { let mut output = vec![]; for val in vals { diff --git a/crates/nu-command/src/filters/length.rs b/crates/nu-command/src/filters/length.rs index 10d202fc6..02282c178 100644 --- a/crates/nu-command/src/filters/length.rs +++ b/crates/nu-command/src/filters/length.rs @@ -107,7 +107,7 @@ fn getcol( .into_pipeline_data(engine_state.ctrlc.clone())) } PipelineData::ListStream(stream, ..) => { - let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect(); + let v: Vec<_> = stream.into_iter().collect(); let input_cols = get_columns(&v); Ok(input_cols diff --git a/crates/nu-command/src/filters/lines.rs b/crates/nu-command/src/filters/lines.rs index 48b3972a7..b4a00f3fb 100644 --- a/crates/nu-command/src/filters/lines.rs +++ b/crates/nu-command/src/filters/lines.rs @@ -69,7 +69,7 @@ impl Command for Lines { let iter = stream .into_iter() - .filter_map(move |(value, _)| { + .filter_map(move |value| { if let Value::String { val, span } = value { if split_char != "\r\n" && val.contains("\r\n") { split_char = "\r\n"; diff --git a/crates/nu-command/src/filters/par_each.rs b/crates/nu-command/src/filters/par_each.rs index 192a06886..b822c5af1 100644 --- a/crates/nu-command/src/filters/par_each.rs +++ b/crates/nu-command/src/filters/par_each.rs @@ -170,7 +170,7 @@ impl Command for ParEach { PipelineData::ListStream(stream, ..) => Ok(stream .enumerate() .par_bridge() - .map(move |(idx, (x, _))| { + .map(move |(idx, x)| { let block = engine_state.get_block(block_id); let mut stack = stack.clone(); diff --git a/crates/nu-command/src/filters/reject.rs b/crates/nu-command/src/filters/reject.rs index b88e38e2d..f86818a18 100644 --- a/crates/nu-command/src/filters/reject.rs +++ b/crates/nu-command/src/filters/reject.rs @@ -125,7 +125,7 @@ fn reject( PipelineData::ListStream(stream, ..) => { let mut output = vec![]; - let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect(); + let v: Vec<_> = stream.into_iter().collect(); let input_cols = get_input_cols(v.clone()); let kc = get_keep_columns(input_cols, columns); keep_columns = get_cellpath_columns(kc, span); diff --git a/crates/nu-command/src/filters/select.rs b/crates/nu-command/src/filters/select.rs index d5d2cd955..7efa0fa6c 100644 --- a/crates/nu-command/src/filters/select.rs +++ b/crates/nu-command/src/filters/select.rs @@ -146,7 +146,7 @@ fn select( .set_metadata(metadata)) } PipelineData::ListStream(stream, metadata, ..) => Ok(stream - .map(move |(x, _)| { + .map(move |x| { if !columns.is_empty() { let mut cols = vec![]; let mut vals = vec![]; diff --git a/crates/nu-command/src/filters/wrap.rs b/crates/nu-command/src/filters/wrap.rs index eef856110..2116dac56 100644 --- a/crates/nu-command/src/filters/wrap.rs +++ b/crates/nu-command/src/filters/wrap.rs @@ -44,7 +44,7 @@ impl Command for Wrap { }) .into_pipeline_data(engine_state.ctrlc.clone())), PipelineData::ListStream(stream, ..) => Ok(stream - .map(move |(x, _)| Value::Record { + .map(move |x| Value::Record { cols: vec![name.clone()], vals: vec![x], span, diff --git a/crates/nu-command/src/math/utils.rs b/crates/nu-command/src/math/utils.rs index fcd272a45..04f73cb74 100644 --- a/crates/nu-command/src/math/utils.rs +++ b/crates/nu-command/src/math/utils.rs @@ -62,9 +62,7 @@ pub fn calculate( mf: impl Fn(&[Value], &Span) -> Result, ) -> Result { match values { - PipelineData::ListStream(s, ..) => { - helper_for_tables(&s.map(|(v, _)| v).collect::>(), name, mf) - } + PipelineData::ListStream(s, ..) => helper_for_tables(&s.collect::>(), name, mf), PipelineData::Value(Value::List { ref vals, .. }, ..) => match &vals[..] { [Value::Record { .. }, _end @ ..] => helper_for_tables(vals, name, mf), _ => mf(vals, &name), diff --git a/crates/nu-command/src/system/complete.rs b/crates/nu-command/src/system/complete.rs index 98baa09de..8e6d142d5 100644 --- a/crates/nu-command/src/system/complete.rs +++ b/crates/nu-command/src/system/complete.rs @@ -70,7 +70,7 @@ impl Command for Complete { } if let Some(exit_code) = exit_code { - let mut v: Vec<_> = exit_code.map(|(v, _)| v).collect(); + let mut v: Vec<_> = exit_code.collect(); if let Some(v) = v.pop() { cols.push("exit_code".to_string()); diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index f5f7e354f..957f821cd 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -90,7 +90,7 @@ prints out the list properly."# } PipelineData::ListStream(stream, ..) => { // dbg!("value::stream"); - let data = convert_to_list(stream.map(|(v, _)| v), config, call.head); + let data = convert_to_list(stream, config, call.head); if let Some(items) = data { Ok(create_grid_output( items, diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index ff405e6c8..465630356 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -266,7 +266,7 @@ fn handle_row_stream( let show_clickable_links = config.show_clickable_links_in_ls && !in_ssh_session; ListStream::from_stream( - stream.map(move |(mut x, _)| match &mut x { + stream.map(move |mut x| match &mut x { Value::Record { cols, vals, .. } => { let mut idx = 0; @@ -564,11 +564,7 @@ impl Iterator for PagingTableCreator { let mut idx = 0; // Pull from stream until time runs out or we have enough items - for (mut item, formatter) in self.stream.by_ref() { - if let Some(formatter) = formatter { - item = formatter.format(item); - } - + for item in self.stream.by_ref() { batch.push(item); idx += 1; @@ -633,70 +629,3 @@ fn load_theme_from_config(config: &Config) -> TableTheme { _ => nu_table::TableTheme::rounded(), } } - -#[cfg(test)] -mod tests { - use nu_protocol::ValueFormatter; - - use super::*; - - #[test] - fn list_stream_value_formatters() { - let span = Span::test_data(); - let ctrlc = None; - let config = Config { - use_ansi_coloring: false, - ..<_>::default() - }; - - let hex_formatter = ValueFormatter::from_fn(|value| { - let (value, span) = match value { - Value::Int { val, span } => (val, span), - _ => return value, - }; - let value = format!("0x{:016x}", value); - - Value::string(value, span) - }); - - let stream = ListStream::from_stream( - [ - (Value::int(42, span), Some(hex_formatter.clone())), - (Value::int(777, span), None), - (Value::int(-1, span), Some(hex_formatter)), - ] - .into_iter(), - ctrlc.clone(), - ); - - let paging_table_creator = PagingTableCreator { - head: span, - stream, - ctrlc, - config, - row_offset: 0, - width_param: Some(80), - }; - - let mut output = Vec::new(); - - for chunk in paging_table_creator { - let chunk = chunk.unwrap(); - - output.extend(chunk); - } - - let output = String::from_utf8(output).unwrap(); - - assert_eq!( - output, - concat!( - "╭───┬────────────────────╮\n", - "│ 0 │ 0x000000000000002a │\n", - "│ 1 │ 777 │\n", - "│ 2 │ 0xffffffffffffffff │\n", - "╰───┴────────────────────╯" - ) - ) - } -} diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 3becbd274..f3e9b2c14 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -254,10 +254,7 @@ fn eval_external( match exit_code { Some(exit_code_stream) => { let ctrlc = exit_code_stream.ctrlc.clone(); - let exit_code: Vec = exit_code_stream - .into_iter() - .map(|(value, _)| value) - .collect(); + let exit_code: Vec = exit_code_stream.into_iter().collect(); if let Some(Value::Int { val: code, .. }) = exit_code.last() { // if exit_code is not 0, it indicates error occured, return back Err. if *code != 0 { @@ -784,7 +781,7 @@ pub fn eval_block( }; if let Some(exit_code) = exit_code { - let mut v: Vec<_> = exit_code.map(|(value, _)| value).collect(); + let mut v: Vec<_> = exit_code.collect(); if let Some(v) = v.pop() { stack.add_env_var("LAST_EXIT_CODE".into(), v); diff --git a/crates/nu-protocol/src/pipeline_data.rs b/crates/nu-protocol/src/pipeline_data.rs index 6925c1e8d..898c73532 100644 --- a/crates/nu-protocol/src/pipeline_data.rs +++ b/crates/nu-protocol/src/pipeline_data.rs @@ -4,7 +4,6 @@ use crate::{ format_error, Config, ListStream, RawStream, ShellError, Span, Value, }; use nu_utils::{stderr_write_all_and_flush, stdout_write_all_and_flush}; -use std::fmt; use std::sync::{atomic::AtomicBool, Arc}; /// The foundational abstraction for input and output to commands @@ -95,7 +94,7 @@ impl PipelineData { PipelineData::Value(Value::Nothing { .. }, ..) => Value::nothing(span), PipelineData::Value(v, ..) => v, PipelineData::ListStream(s, ..) => Value::List { - vals: s.map(|(value, _)| value).collect(), + vals: s.collect(), span, // FIXME? }, PipelineData::ExternalStream { @@ -223,7 +222,7 @@ impl PipelineData { match self { // FIXME: there are probably better ways of doing this PipelineData::ListStream(stream, ..) => Value::List { - vals: stream.map(|(value, _)| value).collect(), + vals: stream.collect(), span: head, } .follow_cell_path(cell_path, insensitive), @@ -241,7 +240,7 @@ impl PipelineData { match self { // FIXME: there are probably better ways of doing this PipelineData::ListStream(stream, ..) => Value::List { - vals: stream.map(|(value, _)| value).collect(), + vals: stream.collect(), span: head, } .upsert_cell_path(cell_path, callback), @@ -264,9 +263,7 @@ impl PipelineData { PipelineData::Value(Value::List { vals, .. }, ..) => { Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc)) } - PipelineData::ListStream(stream, ..) => Ok(stream - .map(move |(value, _)| f(value)) - .into_pipeline_data(ctrlc)), + PipelineData::ListStream(stream, ..) => Ok(stream.map(f).into_pipeline_data(ctrlc)), PipelineData::ExternalStream { stdout: None, .. } => { Ok(PipelineData::new(Span { start: 0, end: 0 })) } @@ -318,9 +315,9 @@ impl PipelineData { PipelineData::Value(Value::List { vals, .. }, ..) => { Ok(vals.into_iter().flat_map(f).into_pipeline_data(ctrlc)) } - PipelineData::ListStream(stream, ..) => Ok(stream - .flat_map(move |(value, _)| f(value)) - .into_pipeline_data(ctrlc)), + PipelineData::ListStream(stream, ..) => { + Ok(stream.flat_map(f).into_pipeline_data(ctrlc)) + } PipelineData::ExternalStream { stdout: None, .. } => { Ok(PipelineData::new(Span { start: 0, end: 0 })) } @@ -369,10 +366,7 @@ impl PipelineData { PipelineData::Value(Value::List { vals, .. }, ..) => { Ok(vals.into_iter().filter(f).into_pipeline_data(ctrlc)) } - PipelineData::ListStream(stream, ..) => Ok(stream - .filter(move |(value, _)| f(value)) - .map(|(value, _)| value) - .into_pipeline_data(ctrlc)), + PipelineData::ListStream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)), PipelineData::ExternalStream { stdout: None, .. } => { Ok(PipelineData::new(Span { start: 0, end: 0 })) } @@ -516,31 +510,6 @@ impl PipelineData { } } -#[derive(Clone)] -pub struct ValueFormatter(Arc Value + Send + Sync>); - -impl ValueFormatter { - pub fn from_fn(f: F) -> Self - where - F: Fn(Value) -> Value, - F: Send + Sync + 'static, - { - Self(Arc::new(f)) - } - - pub fn format(&self, value: Value) -> Value { - self.0(value) - } -} - -impl fmt::Debug for ValueFormatter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("PipelineDataFormatter") - .field(&"") - .finish() - } -} - pub struct PipelineIterator(PipelineData); impl IntoIterator for PipelineData { @@ -553,7 +522,7 @@ impl IntoIterator for PipelineData { PipelineData::Value(Value::List { vals, .. }, metadata) => { PipelineIterator(PipelineData::ListStream( ListStream { - stream: Box::new(vals.into_iter().map(|v| (v, None))), + stream: Box::new(vals.into_iter()), ctrlc: None, }, metadata, @@ -563,14 +532,14 @@ impl IntoIterator for PipelineData { match val.into_range_iter(None) { Ok(iter) => PipelineIterator(PipelineData::ListStream( ListStream { - stream: Box::new(iter.map(|v| (v, None))), + stream: Box::new(iter), ctrlc: None, }, metadata, )), Err(error) => PipelineIterator(PipelineData::ListStream( ListStream { - stream: Box::new(std::iter::once((Value::Error { error }, None))), + stream: Box::new(std::iter::once(Value::Error { error })), ctrlc: None, }, metadata, @@ -589,7 +558,7 @@ impl Iterator for PipelineIterator { match &mut self.0 { PipelineData::Value(Value::Nothing { .. }, ..) => None, PipelineData::Value(v, ..) => Some(std::mem::take(v)), - PipelineData::ListStream(stream, ..) => stream.next().map(|(value, _)| value), + PipelineData::ListStream(stream, ..) => stream.next(), PipelineData::ExternalStream { stdout: None, .. } => None, PipelineData::ExternalStream { stdout: Some(stream), @@ -608,12 +577,10 @@ pub trait IntoPipelineData { impl IntoPipelineData for V where - V: Into<(Value, Option)>, + V: Into, { fn into_pipeline_data(self) -> PipelineData { - let (value, _formatter) = self.into(); - - PipelineData::Value(value, None) + PipelineData::Value(self.into(), None) } } @@ -630,7 +597,7 @@ impl IntoInterruptiblePipelineData for I where I: IntoIterator + Send + 'static, I::IntoIter: Send + 'static, - ::Item: Into<(Value, Option)>, + ::Item: Into, { fn into_pipeline_data(self, ctrlc: Option>) -> PipelineData { PipelineData::ListStream( diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index a219a6b99..b8c9b698b 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -7,8 +7,8 @@ mod unit; use crate::ast::Operator; use crate::ast::{CellPath, PathMember}; +use crate::ShellError; use crate::{did_you_mean, BlockId, Config, Span, Spanned, Type, VarId}; -use crate::{ShellError, ValueFormatter}; use byte_unit::ByteUnit; use chrono::{DateTime, Duration, FixedOffset}; use chrono_humanize::HumanTime; @@ -1127,12 +1127,6 @@ impl Default for Value { } } -impl From for (Value, Option) { - fn from(val: Value) -> Self { - (val, None) - } -} - impl PartialOrd for Value { fn partial_cmp(&self, other: &Self) -> Option { // Compare two floating point numbers. The decision interval for equality is dynamically diff --git a/crates/nu-protocol/src/value/stream.rs b/crates/nu-protocol/src/value/stream.rs index a56b69afa..a111120f3 100644 --- a/crates/nu-protocol/src/value/stream.rs +++ b/crates/nu-protocol/src/value/stream.rs @@ -171,25 +171,23 @@ impl Iterator for RawStream { /// Like other iterators in Rust, observing values from this stream will drain the items as you view them /// and the stream cannot be replayed. pub struct ListStream { - pub stream: Box)> + Send + 'static>, + pub stream: Box + Send + 'static>, pub ctrlc: Option>, } impl ListStream { pub fn into_string(self, separator: &str, config: &Config) -> String { - self.map(|(x, _): (Value, _)| x.into_string(", ", config)) + self.map(|x: Value| x.into_string(", ", config)) .collect::>() .join(separator) } pub fn from_stream( - input: impl Iterator)> + 'static> - + Send - + 'static, + input: impl Iterator + Send + 'static, ctrlc: Option>, ) -> ListStream { ListStream { - stream: Box::new(input.map(Into::into)), + stream: Box::new(input), ctrlc, } } @@ -202,7 +200,7 @@ impl Debug for ListStream { } impl Iterator for ListStream { - type Item = (Value, Option); + type Item = Value; fn next(&mut self) -> Option { if let Some(ctrlc) = &self.ctrlc {