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 03:30:48 +01:00
|
|
|
use crate::data::base::property_get::get_data_by_key;
|
2019-11-19 16:13:10 +01:00
|
|
|
use crate::prelude::*;
|
|
|
|
use csv::WriterBuilder;
|
2019-11-04 16:47:03 +01:00
|
|
|
use indexmap::{indexset, IndexSet};
|
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 03:30:48 +01:00
|
|
|
use nu_errors::ShellError;
|
|
|
|
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
|
2019-11-21 15:33:14 +01:00
|
|
|
use nu_source::Spanned;
|
2019-11-19 16:13:10 +01:00
|
|
|
|
|
|
|
fn from_value_to_delimited_string(
|
2019-11-21 15:33:14 +01:00
|
|
|
tagged_value: &Value,
|
2019-11-19 16:13:10 +01:00
|
|
|
separator: char,
|
|
|
|
) -> Result<String, ShellError> {
|
2019-11-21 15:33:14 +01:00
|
|
|
let v = &tagged_value.value;
|
2019-11-19 16:13:10 +01:00
|
|
|
|
|
|
|
match v {
|
2019-11-21 15:33:14 +01:00
|
|
|
UntaggedValue::Row(o) => {
|
2019-11-19 16:13:10 +01:00
|
|
|
let mut wtr = WriterBuilder::new()
|
|
|
|
.delimiter(separator as u8)
|
|
|
|
.from_writer(vec![]);
|
|
|
|
let mut fields: VecDeque<String> = VecDeque::new();
|
|
|
|
let mut values: VecDeque<String> = VecDeque::new();
|
|
|
|
|
|
|
|
for (k, v) in o.entries.iter() {
|
|
|
|
fields.push_back(k.clone());
|
|
|
|
|
|
|
|
values.push_back(to_string_tagged_value(&v)?);
|
|
|
|
}
|
|
|
|
|
|
|
|
wtr.write_record(fields).expect("can not write.");
|
|
|
|
wtr.write_record(values).expect("can not write.");
|
|
|
|
|
|
|
|
return Ok(String::from_utf8(wtr.into_inner().map_err(|_| {
|
|
|
|
ShellError::labeled_error(
|
|
|
|
"Could not convert record",
|
|
|
|
"original value",
|
|
|
|
&tagged_value.tag,
|
|
|
|
)
|
|
|
|
})?)
|
|
|
|
.map_err(|_| {
|
|
|
|
ShellError::labeled_error(
|
|
|
|
"Could not convert record",
|
|
|
|
"original value",
|
|
|
|
&tagged_value.tag,
|
|
|
|
)
|
|
|
|
})?);
|
|
|
|
}
|
2019-11-21 15:33:14 +01:00
|
|
|
UntaggedValue::Table(list) => {
|
2019-11-19 16:13:10 +01:00
|
|
|
let mut wtr = WriterBuilder::new()
|
|
|
|
.delimiter(separator as u8)
|
|
|
|
.from_writer(vec![]);
|
|
|
|
|
|
|
|
let merged_descriptors = merge_descriptors(&list);
|
2019-11-04 16:47:03 +01:00
|
|
|
|
|
|
|
wtr.write_record(merged_descriptors.iter().map(|item| &item.item[..]))
|
2019-11-19 16:13:10 +01:00
|
|
|
.expect("can not write.");
|
|
|
|
|
|
|
|
for l in list {
|
|
|
|
let mut row = vec![];
|
|
|
|
for desc in &merged_descriptors {
|
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 03:30:48 +01:00
|
|
|
match get_data_by_key(l, desc.borrow_spanned()) {
|
2019-11-19 16:13:10 +01:00
|
|
|
Some(s) => {
|
2019-11-04 16:47:03 +01:00
|
|
|
row.push(to_string_tagged_value(&s)?);
|
2019-11-19 16:13:10 +01:00
|
|
|
}
|
|
|
|
None => {
|
|
|
|
row.push(String::new());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wtr.write_record(&row).expect("can not write");
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(String::from_utf8(wtr.into_inner().map_err(|_| {
|
|
|
|
ShellError::labeled_error(
|
|
|
|
"Could not convert record",
|
|
|
|
"original value",
|
|
|
|
&tagged_value.tag,
|
|
|
|
)
|
|
|
|
})?)
|
|
|
|
.map_err(|_| {
|
|
|
|
ShellError::labeled_error(
|
|
|
|
"Could not convert record",
|
|
|
|
"original value",
|
|
|
|
&tagged_value.tag,
|
|
|
|
)
|
|
|
|
})?);
|
|
|
|
}
|
|
|
|
_ => return to_string_tagged_value(tagged_value),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 15:33:14 +01:00
|
|
|
// NOTE: could this be useful more widely and implemented on Value ?
|
|
|
|
pub fn clone_tagged_value(v: &Value) -> Value {
|
|
|
|
match &v.value {
|
|
|
|
UntaggedValue::Primitive(Primitive::String(s)) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::String(s.clone()))
|
|
|
|
}
|
|
|
|
UntaggedValue::Primitive(Primitive::Nothing) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::Nothing)
|
|
|
|
}
|
|
|
|
UntaggedValue::Primitive(Primitive::Boolean(b)) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::Boolean(b.clone()))
|
|
|
|
}
|
|
|
|
UntaggedValue::Primitive(Primitive::Decimal(f)) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::Decimal(f.clone()))
|
|
|
|
}
|
|
|
|
UntaggedValue::Primitive(Primitive::Int(i)) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::Int(i.clone()))
|
|
|
|
}
|
|
|
|
UntaggedValue::Primitive(Primitive::Path(x)) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::Path(x.clone()))
|
|
|
|
}
|
|
|
|
UntaggedValue::Primitive(Primitive::Bytes(b)) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::Bytes(b.clone()))
|
|
|
|
}
|
|
|
|
UntaggedValue::Primitive(Primitive::Date(d)) => {
|
|
|
|
UntaggedValue::Primitive(Primitive::Date(d.clone()))
|
|
|
|
}
|
|
|
|
UntaggedValue::Row(o) => UntaggedValue::Row(o.clone()),
|
|
|
|
UntaggedValue::Table(l) => UntaggedValue::Table(l.clone()),
|
|
|
|
UntaggedValue::Block(_) => UntaggedValue::Primitive(Primitive::Nothing),
|
|
|
|
_ => UntaggedValue::Primitive(Primitive::Nothing),
|
2019-11-19 16:13:10 +01:00
|
|
|
}
|
2019-11-21 15:33:14 +01:00
|
|
|
.into_value(v.tag.clone())
|
2019-11-19 16:13:10 +01:00
|
|
|
}
|
|
|
|
|
2019-11-21 15:33:14 +01:00
|
|
|
// NOTE: could this be useful more widely and implemented on Value ?
|
|
|
|
fn to_string_tagged_value(v: &Value) -> Result<String, ShellError> {
|
|
|
|
match &v.value {
|
|
|
|
UntaggedValue::Primitive(Primitive::Date(d)) => Ok(d.to_string()),
|
|
|
|
UntaggedValue::Primitive(Primitive::Bytes(b)) => {
|
2019-11-19 16:13:10 +01:00
|
|
|
let tmp = format!("{}", b);
|
|
|
|
Ok(tmp)
|
|
|
|
}
|
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 03:30:48 +01:00
|
|
|
UntaggedValue::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?.to_string()),
|
|
|
|
UntaggedValue::Primitive(Primitive::Decimal(_)) => Ok(v.as_string()?.to_string()),
|
|
|
|
UntaggedValue::Primitive(Primitive::Int(_)) => Ok(v.as_string()?.to_string()),
|
|
|
|
UntaggedValue::Primitive(Primitive::Path(_)) => Ok(v.as_string()?.to_string()),
|
2019-11-21 15:33:14 +01:00
|
|
|
UntaggedValue::Table(_) => return Ok(String::from("[Table]")),
|
|
|
|
UntaggedValue::Row(_) => return Ok(String::from("[Row]")),
|
2019-12-03 07:44:59 +01:00
|
|
|
UntaggedValue::Primitive(Primitive::Line(s)) => return Ok(s.to_string()),
|
2019-11-21 15:33:14 +01:00
|
|
|
UntaggedValue::Primitive(Primitive::String(s)) => return Ok(s.to_string()),
|
2019-11-19 16:13:10 +01:00
|
|
|
_ => {
|
|
|
|
return Err(ShellError::labeled_error(
|
|
|
|
"Unexpected value",
|
|
|
|
"",
|
|
|
|
v.tag.clone(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 15:33:14 +01:00
|
|
|
fn merge_descriptors(values: &[Value]) -> Vec<Spanned<String>> {
|
2019-11-04 16:47:03 +01:00
|
|
|
let mut ret: Vec<Spanned<String>> = vec![];
|
|
|
|
let mut seen: IndexSet<String> = indexset! {};
|
2019-11-19 16:13:10 +01:00
|
|
|
for value in values {
|
|
|
|
for desc in value.data_descriptors() {
|
2019-11-04 16:47:03 +01:00
|
|
|
if !seen.contains(&desc[..]) {
|
|
|
|
seen.insert(desc.clone());
|
|
|
|
ret.push(desc.spanned(value.tag.span));
|
2019-11-19 16:13:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_delimited_data(
|
|
|
|
headerless: bool,
|
|
|
|
sep: char,
|
|
|
|
format_name: &'static str,
|
|
|
|
RunnableContext { input, name, .. }: RunnableContext,
|
|
|
|
) -> Result<OutputStream, ShellError> {
|
|
|
|
let name_tag = name;
|
2019-11-21 15:33:14 +01:00
|
|
|
let name_span = name_tag.span;
|
2019-11-19 16:13:10 +01:00
|
|
|
|
|
|
|
let stream = async_stream! {
|
2019-11-21 15:33:14 +01:00
|
|
|
let input: Vec<Value> = input.values.collect().await;
|
2019-11-19 16:13:10 +01:00
|
|
|
|
|
|
|
let to_process_input = if input.len() > 1 {
|
|
|
|
let tag = input[0].tag.clone();
|
2019-11-21 15:33:14 +01:00
|
|
|
vec![Value { value: UntaggedValue::Table(input), tag } ]
|
2019-11-19 16:13:10 +01:00
|
|
|
} else if input.len() == 1 {
|
|
|
|
input
|
|
|
|
} else {
|
|
|
|
vec![]
|
|
|
|
};
|
|
|
|
|
|
|
|
for value in to_process_input {
|
|
|
|
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
|
|
|
|
Ok(x) => {
|
|
|
|
let converted = if headerless {
|
|
|
|
x.lines().skip(1).collect()
|
|
|
|
} else {
|
|
|
|
x
|
|
|
|
};
|
2019-11-21 15:33:14 +01:00
|
|
|
yield ReturnSuccess::value(UntaggedValue::Primitive(Primitive::String(converted)).into_value(&name_tag))
|
2019-11-19 16:13:10 +01:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let expected = format!("Expected a table with {}-compatible structure.tag() from pipeline", format_name);
|
|
|
|
let requires = format!("requires {}-compatible input", format_name);
|
|
|
|
yield Err(ShellError::labeled_error_with_secondary(
|
|
|
|
expected,
|
|
|
|
requires,
|
2019-11-21 15:33:14 +01:00
|
|
|
name_span,
|
2019-11-19 16:13:10 +01:00
|
|
|
"originates from here".to_string(),
|
2019-11-21 15:33:14 +01:00
|
|
|
value.tag.span,
|
2019-11-19 16:13:10 +01:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(stream.to_output_stream())
|
|
|
|
}
|