table -e Fix stackoverflow (cause endless empty list) (#6847)

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
This commit is contained in:
Maxim Zhiburt 2022-10-22 19:52:32 +03:00 committed by GitHub
parent 89f3cbf318
commit a3dce8ff19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 171 additions and 92 deletions

View File

@ -451,7 +451,7 @@ fn build_expanded_table(
let deep = expand_limit.map(|i| i - 1); let deep = expand_limit.map(|i| i - 1);
let table = convert_to_table2( let table = convert_to_table2(
0, 0,
&vals, vals.iter(),
ctrlc.clone(), ctrlc.clone(),
config, config,
span, span,
@ -775,9 +775,9 @@ fn convert_to_table(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[allow(clippy::into_iter_on_ref)] #[allow(clippy::into_iter_on_ref)]
fn convert_to_table2( fn convert_to_table2<'a>(
row_offset: usize, row_offset: usize,
input: &[Value], input: impl Iterator<Item = &'a Value> + ExactSizeIterator + Clone,
ctrlc: Option<Arc<AtomicBool>>, ctrlc: Option<Arc<AtomicBool>>,
config: &Config, config: &Config,
head: Span, head: Span,
@ -787,13 +787,13 @@ fn convert_to_table2(
flatten: bool, flatten: bool,
flatten_sep: &str, flatten_sep: &str,
) -> Result<Option<NuTable>, ShellError> { ) -> Result<Option<NuTable>, ShellError> {
if input.is_empty() { if input.len() == 0 {
return Ok(None); return Ok(None);
} }
let float_precision = config.float_precision as usize; let float_precision = config.float_precision as usize;
let mut headers = get_columns(input); let mut headers = get_columns(input.clone());
let with_index = match config.table_index_mode { let with_index = match config.table_index_mode {
TableIndexMode::Always => true, TableIndexMode::Always => true,
TableIndexMode::Never => false, TableIndexMode::Never => false,
@ -876,19 +876,19 @@ fn convert_to_table2(
} else { } else {
let skip_num = if with_index { 1 } else { 0 }; let skip_num = if with_index { 1 } else { 0 };
for (col, header) in data[0].iter().enumerate().skip(skip_num) { for (col, header) in data[0].iter().enumerate().skip(skip_num) {
let result = match item { let value = match item {
Value::Record { .. } => item.clone().follow_cell_path( Value::Record { .. } => {
let val = item.clone().follow_cell_path(
&[PathMember::String { &[PathMember::String {
val: header.as_ref().to_owned(), val: header.as_ref().to_owned(),
span: head, span: head,
}], }],
false, false,
), );
_ => Ok(item.clone()),
};
let value = convert_to_table2_entry( match val {
result.ok().as_ref(), Ok(val) => convert_to_table2_entry(
Some(&val),
config, config,
&ctrlc, &ctrlc,
color_hm, color_hm,
@ -898,7 +898,30 @@ fn convert_to_table2(
deep, deep,
flatten, flatten,
flatten_sep, flatten_sep,
); ),
Err(_) => make_styled_string(
item.into_abbreviated_string(config),
&item.get_type().to_string(),
col,
with_index,
color_hm,
float_precision,
),
}
}
_ => convert_to_table2_entry(
Some(item),
config,
&ctrlc,
color_hm,
col,
theme,
with_index,
deep,
flatten,
flatten_sep,
),
};
let value = NuTable::create_cell(value.0, value.1); let value = NuTable::create_cell(value.0, value.1);
row.push(value); row.push(value);
@ -951,28 +974,71 @@ fn convert_to_table2_entry(
} }
}; };
let is_record_or_list = matches!(item, Value::Record { .. } | Value::List { .. }); let is_limit_reached = matches!(deep, Some(0));
let is_simple = matches!(deep, Some(0)) || !is_record_or_list; if is_limit_reached {
return make_styled_string(
let mut text = None; item.into_abbreviated_string(config),
if !is_simple { &item.get_type().to_string(),
let mut arr: [Value; 1] = [Value::default()]; col,
let (list, span): (&[Value], Span) = match item { with_index,
Value::Record { span, .. } => { color_hm,
arr[0] = item.clone(); float_precision,
(&arr, *span) );
} }
Value::List { vals, span } => (vals, *span),
_ => unreachable!("we checked the values already"),
};
let is_simple_list = list match &item {
Value::Record { span, cols, vals } => {
if cols.is_empty() && vals.is_empty() {
make_styled_string(
item.into_abbreviated_string(config),
&item.get_type().to_string(),
col,
with_index,
color_hm,
float_precision,
)
} else {
let table = convert_to_table2(
0,
std::iter::once(item),
ctrlc.clone(),
config,
*span,
color_hm,
theme,
deep.map(|i| i - 1),
flatten,
flatten_sep,
);
let inner_table = table.map(|table| {
table.and_then(|table| {
table.draw_table(config, color_hm, alignments, theme, usize::MAX)
})
});
if let Ok(Some(table)) = inner_table {
(table, TextStyle::default())
} else {
// error so back down to the default
make_styled_string(
item.into_abbreviated_string(config),
&item.get_type().to_string(),
col,
with_index,
color_hm,
float_precision,
)
}
}
}
Value::List { vals, span } => {
let is_simple_list = vals
.iter() .iter()
.all(|v| !matches!(v, Value::Record { .. } | Value::List { .. })); .all(|v| !matches!(v, Value::Record { .. } | Value::List { .. }));
text = if flatten && is_simple_list { if flatten && is_simple_list {
let mut buf = Vec::new(); let mut buf = Vec::new();
for value in list { for value in vals {
let (text, _) = make_styled_string( let (text, _) = make_styled_string(
value.into_abbreviated_string(config), value.into_abbreviated_string(config),
&value.get_type().to_string(), &value.get_type().to_string(),
@ -987,14 +1053,14 @@ fn convert_to_table2_entry(
let text = buf.join(flatten_sep); let text = buf.join(flatten_sep);
Some(text) (text, TextStyle::default())
} else { } else {
let table = convert_to_table2( let table = convert_to_table2(
0, 0,
list, vals.iter(),
ctrlc.clone(), ctrlc.clone(),
config, config,
span, *span,
color_hm, color_hm,
theme, theme,
deep.map(|i| i - 1), deep.map(|i| i - 1),
@ -1002,24 +1068,37 @@ fn convert_to_table2_entry(
flatten_sep, flatten_sep,
); );
if let Ok(Some(table)) = table { let inner_table = table.map(|table| {
table.and_then(|table| {
table.draw_table(config, color_hm, alignments, theme, usize::MAX) table.draw_table(config, color_hm, alignments, theme, usize::MAX)
})
});
if let Ok(Some(table)) = inner_table {
(table, TextStyle::default())
} else { } else {
None // error so back down to the default
} make_styled_string(
}
};
match text {
Some(text) => (text, TextStyle::default()),
None => make_styled_string(
item.into_abbreviated_string(config), item.into_abbreviated_string(config),
&item.get_type().to_string(), &item.get_type().to_string(),
col, col,
with_index, with_index,
color_hm, color_hm,
float_precision, float_precision,
), )
}
}
}
_ => {
// unknown type.
make_styled_string(
item.into_abbreviated_string(config),
&item.get_type().to_string(),
col,
with_index,
color_hm,
float_precision,
)
}
} }
} }
@ -1112,7 +1191,7 @@ impl PagingTableCreator {
let color_hm = get_color_config(&self.config); let color_hm = get_color_config(&self.config);
let table = convert_to_table2( let table = convert_to_table2(
self.row_offset, self.row_offset,
batch, batch.iter(),
self.ctrlc.clone(), self.ctrlc.clone(),
&self.config, &self.config,
self.head, self.head,

View File

@ -1,7 +1,7 @@
use nu_protocol::Value; use nu_protocol::Value;
use std::collections::HashSet; use std::collections::HashSet;
pub fn get_columns(input: &[Value]) -> Vec<String> { pub fn get_columns<'a>(input: impl IntoIterator<Item = &'a Value>) -> Vec<String> {
let mut columns = vec![]; let mut columns = vec![];
for item in input { for item in input {