2019-09-17 07:07:11 +00:00
|
|
|
use crate::commands::WholeStreamCommand;
|
|
|
|
use crate::prelude::*;
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
use nu_errors::ShellError;
|
2019-12-04 19:52:31 +00:00
|
|
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
2019-11-21 14:33:14 +00:00
|
|
|
use nu_source::{SpannedItem, Tagged};
|
2019-12-09 18:52:01 +00:00
|
|
|
use nu_value_ext::get_data_by_key;
|
2019-09-17 07:07:11 +00:00
|
|
|
|
|
|
|
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")
|
2019-10-28 05:15:35 +00:00
|
|
|
.switch("header-row", "treat the first row as column names")
|
|
|
|
.switch("ignore-titles", "don't pivot the column names into values")
|
|
|
|
.rest(
|
|
|
|
SyntaxShape::String,
|
|
|
|
"the names to give columns once pivoted",
|
|
|
|
)
|
2019-09-17 07:07:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
fn merge_descriptors(values: &[Value]) -> Vec<String> {
|
2019-09-17 07:07:11 +00:00
|
|
|
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> {
|
2019-09-26 00:22:17 +00:00
|
|
|
let stream = async_stream! {
|
2019-09-17 07:07:11 +00:00
|
|
|
let input = context.input.into_vec().await;
|
|
|
|
|
|
|
|
let descs = merge_descriptors(&input);
|
|
|
|
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
let mut headers: Vec<String> = vec![];
|
2019-09-17 07:07:11 +00:00
|
|
|
|
|
|
|
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) {
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
match get_data_by_key(&i, desc[..].spanned_unknown()) {
|
2019-09-17 07:07:11 +00:00
|
|
|
Some(x) => {
|
|
|
|
if let Ok(s) = x.as_string() {
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
headers.push(s.to_string());
|
2019-09-17 07:07:11 +00:00
|
|
|
} 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 {
|
2019-12-06 15:28:26 +00:00
|
|
|
for i in 0..=input.len() {
|
2019-09-17 07:07:11 +00:00
|
|
|
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;
|
2019-10-13 04:12:43 +00:00
|
|
|
let mut dict = TaggedDictBuilder::new(&context.name);
|
2019-09-17 07:07:11 +00:00
|
|
|
|
|
|
|
if !args.ignore_titles && !args.header_row {
|
2019-12-04 19:52:31 +00:00
|
|
|
dict.insert_untagged(headers[column_num].clone(), UntaggedValue::string(desc.clone()));
|
2019-09-17 07:07:11 +00:00
|
|
|
column_num += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
for i in input.clone() {
|
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
|
|
|
match get_data_by_key(&i, desc[..].spanned_unknown()) {
|
2019-09-17 07:07:11 +00:00
|
|
|
Some(x) => {
|
2019-11-21 14:33:14 +00:00
|
|
|
dict.insert_value(headers[column_num].clone(), x.clone());
|
2019-09-17 07:07:11 +00:00
|
|
|
}
|
|
|
|
_ => {
|
2019-12-04 19:52:31 +00:00
|
|
|
dict.insert_untagged(headers[column_num].clone(), UntaggedValue::nothing());
|
2019-09-17 07:07:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
column_num += 1;
|
|
|
|
}
|
|
|
|
|
2019-11-21 14:33:14 +00:00
|
|
|
yield ReturnSuccess::value(dict.into_value());
|
2019-09-17 07:07:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(OutputStream::new(stream))
|
|
|
|
}
|