2019-10-13 21:15:30 +02:00
|
|
|
use crate::commands::WholeStreamCommand;
|
|
|
|
use crate::data::{Primitive, TaggedDictBuilder, Value};
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
pub struct FromSSV;
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct FromSSVArgs {
|
|
|
|
headerless: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
const STRING_REPRESENTATION: &str = "from-ssv";
|
|
|
|
|
|
|
|
impl WholeStreamCommand for FromSSV {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
STRING_REPRESENTATION
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
|
|
|
Signature::build(STRING_REPRESENTATION).switch("headerless")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
2019-10-13 23:09:24 +02:00
|
|
|
"Parse text as whitespace-separated values and create a table."
|
2019-10-13 21:15:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn run(
|
|
|
|
&self,
|
|
|
|
args: CommandArgs,
|
|
|
|
registry: &CommandRegistry,
|
|
|
|
) -> Result<OutputStream, ShellError> {
|
|
|
|
args.process(registry, from_ssv)?.run()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-13 22:50:45 +02:00
|
|
|
fn from_ssv_string_to_value(
|
|
|
|
s: &str,
|
|
|
|
headerless: bool,
|
|
|
|
tag: impl Into<Tag>,
|
2019-10-14 07:37:34 +02:00
|
|
|
) -> Option<Tagged<Value>> {
|
2019-10-13 22:50:45 +02:00
|
|
|
let mut lines = s.lines();
|
|
|
|
|
|
|
|
let headers = lines
|
2019-10-14 07:37:34 +02:00
|
|
|
.next()?
|
2019-10-13 22:50:45 +02:00
|
|
|
.split_whitespace()
|
|
|
|
.map(|s| s.to_owned())
|
|
|
|
.collect::<Vec<String>>();
|
|
|
|
|
|
|
|
let header_row = if headerless {
|
|
|
|
(0..headers.len())
|
|
|
|
.map(|i| format!("Column{}", i + 1))
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
} else {
|
|
|
|
headers
|
|
|
|
};
|
|
|
|
|
2019-10-13 23:09:40 +02:00
|
|
|
let tag = tag.into();
|
2019-10-13 22:50:45 +02:00
|
|
|
let rows = lines
|
|
|
|
.map(|l| {
|
|
|
|
let mut row = TaggedDictBuilder::new(tag);
|
|
|
|
for (column, value) in header_row.iter().zip(l.split_whitespace()) {
|
|
|
|
row.insert_tagged(
|
|
|
|
column.to_owned(),
|
|
|
|
Value::Primitive(Primitive::String(String::from(value))).tagged(tag),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
row.into_tagged_value()
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2019-10-14 07:37:34 +02:00
|
|
|
Some(Tagged::from_item(Value::Table(rows), tag))
|
2019-10-13 22:50:45 +02:00
|
|
|
}
|
|
|
|
|
2019-10-13 21:15:30 +02:00
|
|
|
fn from_ssv(
|
2019-10-13 22:50:45 +02:00
|
|
|
FromSSVArgs { headerless }: FromSSVArgs,
|
2019-10-13 21:15:30 +02:00
|
|
|
RunnableContext { input, name, .. }: RunnableContext,
|
|
|
|
) -> Result<OutputStream, ShellError> {
|
2019-10-13 22:50:45 +02:00
|
|
|
let stream = async_stream! {
|
|
|
|
let values: Vec<Tagged<Value>> = input.values.collect().await;
|
|
|
|
let mut concat_string = String::new();
|
|
|
|
let mut latest_tag: Option<Tag> = None;
|
|
|
|
|
|
|
|
for value in values {
|
|
|
|
let value_tag = value.tag();
|
|
|
|
latest_tag = Some(value_tag);
|
|
|
|
match value.item {
|
|
|
|
Value::Primitive(Primitive::String(s)) => {
|
|
|
|
concat_string.push_str(&s);
|
|
|
|
}
|
|
|
|
_ => yield Err(ShellError::labeled_error_with_secondary (
|
|
|
|
"Expected a string from pipeline",
|
|
|
|
"requires string input",
|
|
|
|
name,
|
|
|
|
"value originates from here",
|
|
|
|
value_tag
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match from_ssv_string_to_value(&concat_string, headerless, name) {
|
2019-10-14 07:37:34 +02:00
|
|
|
Some(x) => match x {
|
2019-10-13 22:50:45 +02:00
|
|
|
Tagged { item: Value::Table(list), ..} => {
|
|
|
|
for l in list { yield ReturnSuccess::value(l) }
|
|
|
|
}
|
|
|
|
x => yield ReturnSuccess::value(x)
|
|
|
|
},
|
2019-10-14 07:37:34 +02:00
|
|
|
None => if let Some(last_tag) = latest_tag {
|
2019-10-13 22:50:45 +02:00
|
|
|
yield Err(ShellError::labeled_error_with_secondary(
|
|
|
|
"Could not parse as SSV",
|
|
|
|
"input cannot be parsed ssv",
|
|
|
|
name,
|
|
|
|
"value originates from here",
|
|
|
|
last_tag,
|
|
|
|
))
|
2019-10-14 07:37:34 +02:00
|
|
|
},
|
2019-10-13 22:50:45 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(stream.to_output_stream())
|
2019-10-13 21:15:30 +02:00
|
|
|
}
|