mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
Replace vtable with pivot command
This commit is contained in:
parent
1b42438e8f
commit
f6b82e4c0c
6 changed files with 136 additions and 133 deletions
|
@ -270,13 +270,13 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
per_item_command(Help),
|
per_item_command(Help),
|
||||||
whole_stream_command(Exit),
|
whole_stream_command(Exit),
|
||||||
whole_stream_command(Autoview),
|
whole_stream_command(Autoview),
|
||||||
|
whole_stream_command(Pivot),
|
||||||
per_item_command(Cpy),
|
per_item_command(Cpy),
|
||||||
whole_stream_command(Date),
|
whole_stream_command(Date),
|
||||||
per_item_command(Mkdir),
|
per_item_command(Mkdir),
|
||||||
per_item_command(Move),
|
per_item_command(Move),
|
||||||
whole_stream_command(Save),
|
whole_stream_command(Save),
|
||||||
whole_stream_command(Table),
|
whole_stream_command(Table),
|
||||||
whole_stream_command(VTable),
|
|
||||||
whole_stream_command(Version),
|
whole_stream_command(Version),
|
||||||
whole_stream_command(Which),
|
whole_stream_command(Which),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub(crate) mod next;
|
||||||
pub(crate) mod nth;
|
pub(crate) mod nth;
|
||||||
pub(crate) mod open;
|
pub(crate) mod open;
|
||||||
pub(crate) mod pick;
|
pub(crate) mod pick;
|
||||||
|
pub(crate) mod pivot;
|
||||||
pub(crate) mod plugin;
|
pub(crate) mod plugin;
|
||||||
pub(crate) mod post;
|
pub(crate) mod post;
|
||||||
pub(crate) mod prev;
|
pub(crate) mod prev;
|
||||||
|
@ -62,7 +63,6 @@ pub(crate) mod to_tsv;
|
||||||
pub(crate) mod to_yaml;
|
pub(crate) mod to_yaml;
|
||||||
pub(crate) mod trim;
|
pub(crate) mod trim;
|
||||||
pub(crate) mod version;
|
pub(crate) mod version;
|
||||||
pub(crate) mod vtable;
|
|
||||||
pub(crate) mod where_;
|
pub(crate) mod where_;
|
||||||
pub(crate) mod which_;
|
pub(crate) mod which_;
|
||||||
|
|
||||||
|
@ -105,6 +105,7 @@ pub(crate) use next::Next;
|
||||||
pub(crate) use nth::Nth;
|
pub(crate) use nth::Nth;
|
||||||
pub(crate) use open::Open;
|
pub(crate) use open::Open;
|
||||||
pub(crate) use pick::Pick;
|
pub(crate) use pick::Pick;
|
||||||
|
pub(crate) use pivot::Pivot;
|
||||||
pub(crate) use post::Post;
|
pub(crate) use post::Post;
|
||||||
pub(crate) use prev::Previous;
|
pub(crate) use prev::Previous;
|
||||||
pub(crate) use pwd::PWD;
|
pub(crate) use pwd::PWD;
|
||||||
|
@ -130,6 +131,5 @@ pub(crate) use to_tsv::ToTSV;
|
||||||
pub(crate) use to_yaml::ToYAML;
|
pub(crate) use to_yaml::ToYAML;
|
||||||
pub(crate) use trim::Trim;
|
pub(crate) use trim::Trim;
|
||||||
pub(crate) use version::Version;
|
pub(crate) use version::Version;
|
||||||
pub(crate) use vtable::VTable;
|
|
||||||
pub(crate) use where_::Where;
|
pub(crate) use where_::Where;
|
||||||
pub(crate) use which_::Which;
|
pub(crate) use which_::Which;
|
||||||
|
|
133
src/commands/pivot.rs
Normal file
133
src/commands/pivot.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
use crate::commands::WholeStreamCommand;
|
||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::TaggedDictBuilder;
|
||||||
|
|
||||||
|
pub struct Pivot;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct PivotArgs {
|
||||||
|
rest: Vec<Tagged<String>>,
|
||||||
|
#[serde(rename(deserialize = "header-row"))]
|
||||||
|
header_row: bool,
|
||||||
|
#[serde(rename(deserialize = "ignore-titles"))]
|
||||||
|
ignore_titles: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WholeStreamCommand for Pivot {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"pivot"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("pivot")
|
||||||
|
.switch("header-row")
|
||||||
|
.switch("ignore-titles")
|
||||||
|
.rest(SyntaxShape::String)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Pivots the table contents so rows become columns and columns become rows."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
args.process(registry, pivot)?.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_descriptors(values: &[Tagged<Value>]) -> Vec<String> {
|
||||||
|
let mut ret = vec![];
|
||||||
|
for value in values {
|
||||||
|
for desc in value.data_descriptors() {
|
||||||
|
if !ret.contains(&desc) {
|
||||||
|
ret.push(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||||
|
let stream = async_stream_block! {
|
||||||
|
let input = context.input.into_vec().await;
|
||||||
|
|
||||||
|
let descs = merge_descriptors(&input);
|
||||||
|
|
||||||
|
let mut headers = vec![];
|
||||||
|
|
||||||
|
if args.rest.len() > 0 && args.header_row {
|
||||||
|
yield Err(ShellError::labeled_error("Can not provide header names and use header row", "using header row", context.name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.header_row {
|
||||||
|
for i in input.clone() {
|
||||||
|
if let Some(desc) = descs.get(0) {
|
||||||
|
match i.get_data_by_key(&desc) {
|
||||||
|
Some(x) => {
|
||||||
|
if let Ok(s) = x.as_string() {
|
||||||
|
headers.push(s);
|
||||||
|
} else {
|
||||||
|
yield Err(ShellError::labeled_error("Header row needs string headers", "used non-string headers", context.name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
yield Err(ShellError::labeled_error("Header row is incomplete and can't be used", "using incomplete header row", context.name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
yield Err(ShellError::labeled_error("Header row is incomplete and can't be used", "using incomplete header row", context.name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i in 0..input.len()+1 {
|
||||||
|
if let Some(name) = args.rest.get(i) {
|
||||||
|
headers.push(name.to_string())
|
||||||
|
} else {
|
||||||
|
headers.push(format!("Column{}", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let descs: Vec<_> = if args.header_row {
|
||||||
|
descs.iter().skip(1).collect()
|
||||||
|
} else {
|
||||||
|
descs.iter().collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
for desc in descs {
|
||||||
|
let mut column_num: usize = 0;
|
||||||
|
let mut dict = TaggedDictBuilder::new(context.name);
|
||||||
|
|
||||||
|
if !args.ignore_titles && !args.header_row {
|
||||||
|
dict.insert(headers[column_num].clone(), Value::string(desc.clone()));
|
||||||
|
column_num += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in input.clone() {
|
||||||
|
match i.get_data_by_key(&desc) {
|
||||||
|
Some(x) => {
|
||||||
|
dict.insert_tagged(headers[column_num].clone(), x.clone());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
dict.insert(headers[column_num].clone(), Value::nothing());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
column_num += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield ReturnSuccess::value(dict.into_tagged_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(OutputStream::new(stream))
|
||||||
|
}
|
|
@ -1,47 +0,0 @@
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::errors::ShellError;
|
|
||||||
use crate::format::VTableView;
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
pub struct VTable;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct VTableArgs {}
|
|
||||||
|
|
||||||
impl WholeStreamCommand for VTable {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"vtable"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("vtable")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"View the contents of the pipeline as a vertical (rotated) table."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
args.process(registry, vtable)?.run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vtable(_args: VTableArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
|
||||||
let stream = async_stream_block! {
|
|
||||||
let input = context.input.into_vec().await;
|
|
||||||
|
|
||||||
if input.len() > 0 {
|
|
||||||
let mut host = context.host.lock().unwrap();
|
|
||||||
let view = VTableView::from_list(&input);
|
|
||||||
if let Some(view) = view {
|
|
||||||
handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::new(stream))
|
|
||||||
}
|
|
|
@ -2,14 +2,12 @@ pub(crate) mod entries;
|
||||||
pub(crate) mod generic;
|
pub(crate) mod generic;
|
||||||
pub(crate) mod list;
|
pub(crate) mod list;
|
||||||
pub(crate) mod table;
|
pub(crate) mod table;
|
||||||
pub(crate) mod vtable;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub(crate) use entries::EntriesView;
|
pub(crate) use entries::EntriesView;
|
||||||
|
|
||||||
pub(crate) use table::TableView;
|
pub(crate) use table::TableView;
|
||||||
pub(crate) use vtable::VTableView;
|
|
||||||
|
|
||||||
pub(crate) trait RenderView {
|
pub(crate) trait RenderView {
|
||||||
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>;
|
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>;
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
use crate::data::Value;
|
|
||||||
use crate::format::RenderView;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use derive_new::new;
|
|
||||||
|
|
||||||
use prettytable::format::{FormatBuilder, LinePosition, LineSeparator};
|
|
||||||
use prettytable::{color, Attr, Cell, Row, Table};
|
|
||||||
|
|
||||||
#[derive(new)]
|
|
||||||
pub struct VTableView {
|
|
||||||
entries: Vec<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VTableView {
|
|
||||||
pub fn from_list(values: &[Tagged<Value>]) -> Option<VTableView> {
|
|
||||||
if values.len() == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let item = &values[0];
|
|
||||||
let headers = item.data_descriptors();
|
|
||||||
|
|
||||||
if headers.len() == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entries = vec![];
|
|
||||||
|
|
||||||
for header in headers {
|
|
||||||
let mut row = vec![];
|
|
||||||
|
|
||||||
row.push(header.clone());
|
|
||||||
for value in values {
|
|
||||||
row.push(value.get_data(&header).borrow().format_leaf(Some(&header)));
|
|
||||||
}
|
|
||||||
entries.push(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(VTableView { entries })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderView for VTableView {
|
|
||||||
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
|
|
||||||
if self.entries.len() == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut table = Table::new();
|
|
||||||
table.set_format(
|
|
||||||
FormatBuilder::new()
|
|
||||||
.column_separator('│')
|
|
||||||
.separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' '))
|
|
||||||
.separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' '))
|
|
||||||
.separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' '))
|
|
||||||
.padding(1, 1)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for row in &self.entries {
|
|
||||||
table.add_row(Row::new(
|
|
||||||
row.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, h)| {
|
|
||||||
if idx == 0 {
|
|
||||||
Cell::new(h)
|
|
||||||
.with_style(Attr::ForegroundColor(color::GREEN))
|
|
||||||
.with_style(Attr::Bold)
|
|
||||||
} else {
|
|
||||||
Cell::new(h)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
table.print_term(&mut *host.out_terminal()).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue