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,29 +876,52 @@ 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 { .. } => {
&[PathMember::String { let val = item.clone().follow_cell_path(
val: header.as_ref().to_owned(), &[PathMember::String {
span: head, val: header.as_ref().to_owned(),
}], 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(
config, Some(&val),
&ctrlc, config,
color_hm, &ctrlc,
col, color_hm,
theme, col,
with_index, theme,
deep, with_index,
flatten, deep,
flatten_sep, flatten,
); 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,75 +974,131 @@ 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;
if !is_simple {
let mut arr: [Value; 1] = [Value::default()];
let (list, span): (&[Value], Span) = match item {
Value::Record { span, .. } => {
arr[0] = item.clone();
(&arr, *span)
}
Value::List { vals, span } => (vals, *span),
_ => unreachable!("we checked the values already"),
};
let is_simple_list = list
.iter()
.all(|v| !matches!(v, Value::Record { .. } | Value::List { .. }));
text = if flatten && is_simple_list {
let mut buf = Vec::new();
for value in list {
let (text, _) = make_styled_string(
value.into_abbreviated_string(config),
&value.get_type().to_string(),
col,
with_index,
color_hm,
float_precision,
);
buf.push(text);
}
let text = buf.join(flatten_sep);
Some(text)
} else {
let table = convert_to_table2(
0,
list,
ctrlc.clone(),
config,
span,
color_hm,
theme,
deep.map(|i| i - 1),
flatten,
flatten_sep,
);
if let Ok(Some(table)) = table {
table.draw_table(config, color_hm, alignments, theme, usize::MAX)
} else {
None
}
}
};
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,
), );
}
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()
.all(|v| !matches!(v, Value::Record { .. } | Value::List { .. }));
if flatten && is_simple_list {
let mut buf = Vec::new();
for value in vals {
let (text, _) = make_styled_string(
value.into_abbreviated_string(config),
&value.get_type().to_string(),
col,
with_index,
color_hm,
float_precision,
);
buf.push(text);
}
let text = buf.join(flatten_sep);
(text, TextStyle::default())
} else {
let table = convert_to_table2(
0,
vals.iter(),
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,
)
}
}
}
_ => {
// 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 {