nushell/crates/nu-command/src/viewers/table.rs

173 lines
5.4 KiB
Rust
Raw Normal View History

2021-09-29 20:25:05 +02:00
use nu_protocol::ast::{Call, PathMember};
use nu_protocol::engine::{Command, EvaluationContext};
use nu_protocol::{Signature, Span, Value};
use nu_table::StyledString;
2021-10-08 15:14:32 +02:00
use std::collections::HashMap;
use terminal_size::{Height, Width};
2021-09-29 20:25:05 +02:00
pub struct Table;
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
impl Command for Table {
fn name(&self) -> &str {
"table"
}
fn usage(&self) -> &str {
"Render the table."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("table")
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
2021-10-08 15:14:32 +02:00
let term_width = if let Some((Width(w), Height(_h))) = terminal_size::terminal_size() {
w as usize
} else {
80usize
};
2021-09-29 20:25:05 +02:00
match input {
Value::List { vals, .. } => {
let table = convert_to_table(vals);
if let Some(table) = table {
2021-10-08 15:14:32 +02:00
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
2021-09-29 20:25:05 +02:00
Ok(Value::String {
val: result,
span: call.head,
})
} else {
Ok(Value::Nothing { span: call.head })
}
}
Value::Stream { stream, .. } => {
let table = convert_to_table(stream);
if let Some(table) = table {
2021-10-08 15:14:32 +02:00
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
2021-09-29 20:25:05 +02:00
Ok(Value::String {
val: result,
span: call.head,
})
} else {
Ok(Value::Nothing { span: call.head })
}
}
2021-10-01 08:01:22 +02:00
Value::Record { cols, vals, .. } => {
let mut output = vec![];
for (c, v) in cols.into_iter().zip(vals.into_iter()) {
output.push(vec![
StyledString {
contents: c,
2021-10-02 04:59:11 +02:00
style: nu_table::TextStyle::default_field(),
2021-10-01 08:01:22 +02:00
},
StyledString {
contents: v.into_string(),
style: nu_table::TextStyle::default(),
},
])
}
let table = nu_table::Table {
headers: vec![],
data: output,
theme: nu_table::Theme::rounded(),
};
2021-10-08 15:14:32 +02:00
let result = nu_table::draw_table(&table, term_width, &HashMap::new());
2021-10-01 08:01:22 +02:00
Ok(Value::String {
val: result,
span: call.head,
})
}
2021-09-29 20:25:05 +02:00
x => Ok(x),
}
}
}
fn convert_to_table(iter: impl IntoIterator<Item = Value>) -> Option<nu_table::Table> {
let mut iter = iter.into_iter().peekable();
if let Some(first) = iter.peek() {
let mut headers = first.columns();
if !headers.is_empty() {
headers.insert(0, "#".into());
}
let mut data = vec![];
for (row_num, item) in iter.enumerate() {
let mut row = vec![row_num.to_string()];
if headers.is_empty() {
row.push(item.into_string())
} else {
for header in headers.iter().skip(1) {
let result = match item {
Value::Record { .. } => {
item.clone().follow_cell_path(&[PathMember::String {
val: header.into(),
span: Span::unknown(),
}])
}
_ => Ok(item.clone()),
};
match result {
Ok(value) => row.push(value.into_string()),
Err(_) => row.push(String::new()),
}
}
}
data.push(row);
}
Some(nu_table::Table {
headers: headers
.into_iter()
.map(|x| StyledString {
contents: x,
style: nu_table::TextStyle::default_header(),
})
.collect(),
data: data
.into_iter()
.map(|x| {
x.into_iter()
.enumerate()
.map(|(col, y)| {
if col == 0 {
StyledString {
contents: y,
style: nu_table::TextStyle::default_header(),
}
} else {
StyledString {
contents: y,
style: nu_table::TextStyle::basic_left(),
}
}
})
.collect::<Vec<StyledString>>()
})
.collect(),
theme: nu_table::Theme::rounded(),
})
} else {
None
}
}