Merge branch 'master' into source_spans

This commit is contained in:
Jonathan Turner
2019-07-20 13:14:05 +12:00
11 changed files with 134 additions and 9 deletions

View File

@ -156,6 +156,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("cd", Box::new(cd::cd)),
command("first", Box::new(first::first)),
command("size", Box::new(size::size)),
command("from-csv", Box::new(from_csv::from_csv)),
command("from-ini", Box::new(from_ini::from_ini)),
command("from-json", Box::new(from_json::from_json)),
command("from-toml", Box::new(from_toml::from_toml)),

View File

@ -11,6 +11,7 @@ crate mod command;
crate mod config;
crate mod exit;
crate mod first;
crate mod from_csv;
crate mod from_ini;
crate mod from_json;
crate mod from_toml;

68
src/commands/from_csv.rs Normal file
View File

@ -0,0 +1,68 @@
use crate::object::{Primitive, SpannedDictBuilder, SpannedListBuilder, Value};
use crate::prelude::*;
use csv::ReaderBuilder;
pub fn from_csv_string_to_value(
s: String,
span: impl Into<Span>,
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
let mut reader = ReaderBuilder::new().has_headers(false).from_reader(s.as_bytes());
let span = span.into();
let mut fields: VecDeque<String> = VecDeque::new();
let mut iter = reader.records();
let mut root = SpannedDictBuilder::new(span);
let mut rows = SpannedListBuilder::new(span);
if let Some(result) = iter.next() {
let line = result?;
for item in line.iter() {
fields.push_back(item.to_string());
}
}
loop {
if let Some(row_values) = iter.next() {
let row_values = row_values?;
let mut row = SpannedDictBuilder::new(span);
for (idx, entry) in row_values.iter().enumerate() {
row.insert_spanned(fields.get(idx).unwrap(), Value::Primitive(Primitive::String(String::from(entry))).spanned(span));
}
rows.insert_spanned(row.into_spanned_value());
} else {
break;
}
}
root.insert_spanned("root", rows.into_spanned_value());
Ok(root.into_spanned_value())
}
pub fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input;
let span = args.name_span;
Ok(out
.values
.map(move |a| match a.item {
Value::Primitive(Primitive::String(s)) => match from_csv_string_to_value(s, span) {
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as CSV",
"piped data failed CSV parse",
span,
)),
},
_ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)),
})
.to_output_stream())
}

View File

@ -196,6 +196,17 @@ pub fn parse_as_value(
name_span: Option<Span>,
) -> Result<Spanned<Value>, ShellError> {
match extension {
Some(x) if x == "csv" => {
crate::commands::from_csv::from_csv_string_to_value(contents, contents_span)
.map(|c| c.spanned(contents_span))
.map_err(move |_| {
ShellError::maybe_labeled_error(
"Could not open as CSV",
"could not open as CSV",
name_span,
)
})
}
Some(x) if x == "toml" => {
crate::commands::from_toml::from_toml_string_to_value(contents, contents_span)
.map(|c| c.spanned(contents_span))

View File

@ -7,5 +7,5 @@ crate mod process;
crate mod types;
crate use base::{Block, Primitive, Switch, Value};
crate use dict::{Dictionary, SpannedDictBuilder};
crate use dict::{Dictionary, SpannedDictBuilder, SpannedListBuilder};
crate use files::dir_entry_dict;