mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 22:50:14 +02:00
Add support for optional list stream output formatting (#6325)
* add support for optional list stream output formatting * cargo fmt * table: add ValueFormatter test
This commit is contained in:
@ -34,28 +34,32 @@ impl Command for Echo {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
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,
|
||||
),
|
||||
call.rest(engine_state, stack, 0)
|
||||
.map(|to_be_echoed: Vec<Value>| {
|
||||
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<Example> {
|
||||
|
@ -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(
|
||||
|
@ -91,7 +91,7 @@ fn getcol(
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
let v: Vec<_> = stream.into_iter().collect();
|
||||
let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect();
|
||||
let input_cols = get_columns(&v);
|
||||
|
||||
Ok(input_cols
|
||||
|
@ -115,7 +115,7 @@ fn dropcol(
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
let mut output = vec![];
|
||||
|
||||
let v: Vec<_> = stream.into_iter().collect();
|
||||
let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect();
|
||||
let input_cols = get_input_cols(v.clone());
|
||||
let kc = get_keep_columns(input_cols, columns);
|
||||
keep_columns = get_cellpath_columns(kc, span);
|
||||
|
@ -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 {
|
||||
|
@ -107,7 +107,7 @@ fn getcol(
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
let v: Vec<_> = stream.into_iter().collect();
|
||||
let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect();
|
||||
let input_cols = get_columns(&v);
|
||||
|
||||
Ok(input_cols
|
||||
|
@ -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";
|
||||
|
@ -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();
|
||||
|
@ -125,7 +125,7 @@ fn reject(
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
let mut output = vec![];
|
||||
|
||||
let v: Vec<_> = stream.into_iter().collect();
|
||||
let v: Vec<_> = stream.into_iter().map(|(v, _)| v).collect();
|
||||
let input_cols = get_input_cols(v.clone());
|
||||
let kc = get_keep_columns(input_cols, columns);
|
||||
keep_columns = get_cellpath_columns(kc, span);
|
||||
|
@ -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![];
|
||||
|
@ -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,
|
||||
|
@ -62,7 +62,9 @@ pub fn calculate(
|
||||
mf: impl Fn(&[Value], &Span) -> Result<Value, ShellError>,
|
||||
) -> Result<Value, ShellError> {
|
||||
match values {
|
||||
PipelineData::ListStream(s, ..) => helper_for_tables(&s.collect::<Vec<Value>>(), name, mf),
|
||||
PipelineData::ListStream(s, ..) => {
|
||||
helper_for_tables(&s.map(|(v, _)| v).collect::<Vec<Value>>(), name, mf)
|
||||
}
|
||||
PipelineData::Value(Value::List { ref vals, .. }, ..) => match &vals[..] {
|
||||
[Value::Record { .. }, _end @ ..] => helper_for_tables(vals, name, mf),
|
||||
_ => mf(vals, &name),
|
||||
|
@ -70,7 +70,7 @@ impl Command for Complete {
|
||||
}
|
||||
|
||||
if let Some(exit_code) = exit_code {
|
||||
let mut v: Vec<_> = exit_code.collect();
|
||||
let mut v: Vec<_> = exit_code.map(|(v, _)| v).collect();
|
||||
|
||||
if let Some(v) = v.pop() {
|
||||
cols.push("exit_code".to_string());
|
||||
|
@ -90,7 +90,7 @@ prints out the list properly."#
|
||||
}
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
// dbg!("value::stream");
|
||||
let data = convert_to_list(stream, config, call.head);
|
||||
let data = convert_to_list(stream.map(|(v, _)| v), config, call.head);
|
||||
if let Some(items) = data {
|
||||
Ok(create_grid_output(
|
||||
items,
|
||||
|
@ -260,7 +260,7 @@ fn handle_row_stream(
|
||||
let ls_colors = get_ls_colors(ls_colors_env_str);
|
||||
|
||||
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;
|
||||
|
||||
@ -512,7 +512,11 @@ impl Iterator for PagingTableCreator {
|
||||
let mut idx = 0;
|
||||
|
||||
// Pull from stream until time runs out or we have enough items
|
||||
for item in self.stream.by_ref() {
|
||||
for (mut item, formatter) in self.stream.by_ref() {
|
||||
if let Some(formatter) = formatter {
|
||||
item = formatter.format(item);
|
||||
}
|
||||
|
||||
batch.push(item);
|
||||
idx += 1;
|
||||
|
||||
@ -577,3 +581,70 @@ 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",
|
||||
"╰───┴────────────────────╯"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user