2021-09-29 20:25:05 +02:00
|
|
|
use nu_protocol::ast::{Call, PathMember};
|
2021-10-25 18:58:58 +02:00
|
|
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
2021-11-17 05:22:37 +01:00
|
|
|
use nu_protocol::{
|
|
|
|
Category, Config, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
|
|
|
|
};
|
2021-11-14 20:25:57 +01:00
|
|
|
use nu_table::{StyledString, Theme};
|
2021-10-08 15:14:32 +02:00
|
|
|
use std::collections::HashMap;
|
2021-11-09 07:14:00 +01:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::Arc;
|
2021-10-08 15:14:32 +02:00
|
|
|
use terminal_size::{Height, Width};
|
2021-09-29 20:25:05 +02:00
|
|
|
|
2021-10-25 06:01:02 +02:00
|
|
|
#[derive(Clone)]
|
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 {
|
2021-11-17 05:22:37 +01:00
|
|
|
Signature::build("table").category(Category::Viewers)
|
2021-09-29 20:25:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn run(
|
|
|
|
&self,
|
2021-11-09 07:14:00 +01:00
|
|
|
engine_state: &EngineState,
|
2021-11-14 20:25:57 +01:00
|
|
|
stack: &mut Stack,
|
2021-09-29 20:25:05 +02:00
|
|
|
call: &Call,
|
2021-10-25 06:24:10 +02:00
|
|
|
input: PipelineData,
|
|
|
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
2021-11-09 07:14:00 +01:00
|
|
|
let ctrlc = engine_state.ctrlc.clone();
|
2021-11-14 20:25:57 +01:00
|
|
|
let config = stack.get_config()?;
|
2021-11-09 07:14:00 +01:00
|
|
|
|
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 {
|
2021-10-25 06:24:10 +02:00
|
|
|
PipelineData::Value(Value::List { vals, .. }) => {
|
2021-11-14 20:25:57 +01:00
|
|
|
let table = convert_to_table(vals, ctrlc, &config)?;
|
2021-09-29 20:25:05 +02:00
|
|
|
|
|
|
|
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,
|
2021-10-25 06:24:10 +02:00
|
|
|
}
|
|
|
|
.into_pipeline_data())
|
2021-09-29 20:25:05 +02:00
|
|
|
} else {
|
2021-11-06 06:50:33 +01:00
|
|
|
Ok(PipelineData::new(call.head))
|
2021-09-29 20:25:05 +02:00
|
|
|
}
|
|
|
|
}
|
2021-10-25 06:24:10 +02:00
|
|
|
PipelineData::Stream(stream) => {
|
2021-11-14 20:25:57 +01:00
|
|
|
let table = convert_to_table(stream, ctrlc, &config)?;
|
2021-09-29 20:25:05 +02:00
|
|
|
|
|
|
|
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,
|
2021-10-25 06:24:10 +02:00
|
|
|
}
|
|
|
|
.into_pipeline_data())
|
2021-09-29 20:25:05 +02:00
|
|
|
} else {
|
2021-11-06 06:50:33 +01:00
|
|
|
Ok(PipelineData::new(call.head))
|
2021-09-29 20:25:05 +02:00
|
|
|
}
|
|
|
|
}
|
2021-10-25 06:24:10 +02:00
|
|
|
PipelineData::Value(Value::Record { cols, vals, .. }) => {
|
2021-10-01 08:01:22 +02:00
|
|
|
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 {
|
2021-11-14 20:25:57 +01:00
|
|
|
contents: v.into_string(", ", &config),
|
2021-10-01 08:01:22 +02:00
|
|
|
style: nu_table::TextStyle::default(),
|
|
|
|
},
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
let table = nu_table::Table {
|
|
|
|
headers: vec![],
|
|
|
|
data: output,
|
2021-11-14 20:25:57 +01:00
|
|
|
theme: load_theme_from_config(&config),
|
2021-10-01 08:01:22 +02:00
|
|
|
};
|
|
|
|
|
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-10-25 06:24:10 +02:00
|
|
|
}
|
|
|
|
.into_pipeline_data())
|
2021-10-01 08:01:22 +02:00
|
|
|
}
|
2021-10-25 06:24:10 +02:00
|
|
|
PipelineData::Value(Value::Error { error }) => Err(error),
|
2021-09-29 20:25:05 +02:00
|
|
|
x => Ok(x),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 19:35:40 +02:00
|
|
|
fn convert_to_table(
|
|
|
|
iter: impl IntoIterator<Item = Value>,
|
2021-11-09 07:14:00 +01:00
|
|
|
ctrlc: Option<Arc<AtomicBool>>,
|
2021-11-14 20:25:57 +01:00
|
|
|
config: &Config,
|
2021-10-11 19:35:40 +02:00
|
|
|
) -> Result<Option<nu_table::Table>, ShellError> {
|
2021-09-29 20:25:05 +02:00
|
|
|
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() {
|
2021-11-09 07:14:00 +01:00
|
|
|
if let Some(ctrlc) = &ctrlc {
|
|
|
|
if ctrlc.load(Ordering::SeqCst) {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
}
|
2021-10-11 19:35:40 +02:00
|
|
|
if let Value::Error { error } = item {
|
|
|
|
return Err(error);
|
|
|
|
}
|
2021-09-29 20:25:05 +02:00
|
|
|
let mut row = vec![row_num.to_string()];
|
|
|
|
|
|
|
|
if headers.is_empty() {
|
2021-11-14 20:25:57 +01:00
|
|
|
row.push(item.into_string(", ", config))
|
2021-09-29 20:25:05 +02:00
|
|
|
} 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 {
|
2021-11-14 20:25:57 +01:00
|
|
|
Ok(value) => row.push(value.into_string(", ", config)),
|
2021-09-29 20:25:05 +02:00
|
|
|
Err(_) => row.push(String::new()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data.push(row);
|
|
|
|
}
|
|
|
|
|
2021-10-11 19:35:40 +02:00
|
|
|
Ok(Some(nu_table::Table {
|
2021-09-29 20:25:05 +02:00
|
|
|
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(),
|
2021-11-14 20:25:57 +01:00
|
|
|
theme: load_theme_from_config(config),
|
2021-10-11 19:35:40 +02:00
|
|
|
}))
|
2021-09-29 20:25:05 +02:00
|
|
|
} else {
|
2021-10-11 19:35:40 +02:00
|
|
|
Ok(None)
|
2021-09-29 20:25:05 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-14 20:25:57 +01:00
|
|
|
|
|
|
|
fn load_theme_from_config(config: &Config) -> Theme {
|
|
|
|
match config.table_mode.as_str() {
|
|
|
|
"basic" => nu_table::Theme::basic(),
|
|
|
|
"compact" => nu_table::Theme::compact(),
|
|
|
|
"compact_double" => nu_table::Theme::compact_double(),
|
|
|
|
"light" => nu_table::Theme::light(),
|
|
|
|
"with_love" => nu_table::Theme::with_love(),
|
|
|
|
"rounded" => nu_table::Theme::rounded(),
|
|
|
|
"reinforced" => nu_table::Theme::reinforced(),
|
|
|
|
"heavy" => nu_table::Theme::heavy(),
|
|
|
|
"none" => nu_table::Theme::none(),
|
|
|
|
_ => nu_table::Theme::rounded(),
|
|
|
|
}
|
|
|
|
}
|