Merge pull request #546 from jonathandturner/from_to_automatic

Automatically call matching from-* and to-* commands
This commit is contained in:
Jonathan Turner 2019-08-31 14:17:32 +12:00 committed by GitHub
commit 339cf98dd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 330 additions and 334 deletions

View file

@ -17,7 +17,7 @@ use crate::prelude::*;
use log::{debug, trace}; use log::{debug, trace};
use regex::Regex; use regex::Regex;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::{self, ColorMode, Config, Editor, config::Configurer, config::EditMode}; use rustyline::{self, config::Configurer, config::EditMode, ColorMode, Config, Editor};
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::io::{BufRead, BufReader, Write}; use std::io::{BufRead, BufReader, Write};
@ -180,6 +180,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
whole_stream_command(ToCSV), whole_stream_command(ToCSV),
whole_stream_command(ToJSON), whole_stream_command(ToJSON),
whole_stream_command(ToSQLite), whole_stream_command(ToSQLite),
whole_stream_command(ToDB),
whole_stream_command(ToTOML), whole_stream_command(ToTOML),
whole_stream_command(ToTSV), whole_stream_command(ToTSV),
whole_stream_command(ToYAML), whole_stream_command(ToYAML),
@ -194,10 +195,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
whole_stream_command(FromINI), whole_stream_command(FromINI),
whole_stream_command(FromBSON), whole_stream_command(FromBSON),
whole_stream_command(FromJSON), whole_stream_command(FromJSON),
whole_stream_command(FromDB),
whole_stream_command(FromSQLite), whole_stream_command(FromSQLite),
whole_stream_command(FromTOML), whole_stream_command(FromTOML),
whole_stream_command(FromXML), whole_stream_command(FromXML),
whole_stream_command(FromYAML), whole_stream_command(FromYAML),
whole_stream_command(FromYML),
whole_stream_command(Pick), whole_stream_command(Pick),
whole_stream_command(Get), whole_stream_command(Get),
per_item_command(Remove), per_item_command(Remove),

View file

@ -82,11 +82,13 @@ pub(crate) use from_bson::FromBSON;
pub(crate) use from_csv::FromCSV; pub(crate) use from_csv::FromCSV;
pub(crate) use from_ini::FromINI; pub(crate) use from_ini::FromINI;
pub(crate) use from_json::FromJSON; pub(crate) use from_json::FromJSON;
pub(crate) use from_sqlite::FromDB;
pub(crate) use from_sqlite::FromSQLite; pub(crate) use from_sqlite::FromSQLite;
pub(crate) use from_toml::FromTOML; pub(crate) use from_toml::FromTOML;
pub(crate) use from_tsv::FromTSV; pub(crate) use from_tsv::FromTSV;
pub(crate) use from_xml::FromXML; pub(crate) use from_xml::FromXML;
pub(crate) use from_yaml::FromYAML; pub(crate) use from_yaml::FromYAML;
pub(crate) use from_yaml::FromYML;
pub(crate) use get::Get; pub(crate) use get::Get;
pub(crate) use last::Last; pub(crate) use last::Last;
pub(crate) use lines::Lines; pub(crate) use lines::Lines;
@ -115,6 +117,7 @@ pub(crate) use to_array::ToArray;
pub(crate) use to_bson::ToBSON; pub(crate) use to_bson::ToBSON;
pub(crate) use to_csv::ToCSV; pub(crate) use to_csv::ToCSV;
pub(crate) use to_json::ToJSON; pub(crate) use to_json::ToJSON;
pub(crate) use to_sqlite::ToDB;
pub(crate) use to_sqlite::ToSQLite; pub(crate) use to_sqlite::ToSQLite;
pub(crate) use to_toml::ToTOML; pub(crate) use to_toml::ToTOML;
pub(crate) use to_tsv::ToTSV; pub(crate) use to_tsv::ToTSV;

View file

@ -146,53 +146,9 @@ impl InternalCommand {
.insert_at_current(Box::new(ValueShell::new(value))); .insert_at_current(Box::new(ValueShell::new(value)));
} }
CommandAction::EnterShell(location) => { CommandAction::EnterShell(location) => {
let path = std::path::Path::new(&location); context.shell_manager.insert_at_current(Box::new(
FilesystemShell::with_location(location, context.registry().clone())?,
if path.is_dir() { ));
// If it's a directory, add a new filesystem shell
context.shell_manager.insert_at_current(Box::new(
FilesystemShell::with_location(
location,
context.registry().clone(),
)?,
));
} else {
// If it's a file, attempt to open the file as a value and enter it
let cwd = context.shell_manager.path();
let full_path = std::path::PathBuf::from(cwd);
let (file_extension, contents, contents_tag, span_source) =
crate::commands::open::fetch(
&full_path,
&location,
Span::unknown(),
)
.await?;
if let Some(uuid) = contents_tag.origin {
// If we have loaded something, track its source
context.add_span_source(uuid, span_source);
}
match contents {
Value::Primitive(Primitive::String(string)) => {
let value = crate::commands::open::parse_string_as_value(
file_extension,
string,
contents_tag,
Span::unknown(),
)?;
context
.shell_manager
.insert_at_current(Box::new(ValueShell::new(value)));
}
value => context.shell_manager.insert_at_current(Box::new(
ValueShell::new(value.tagged(contents_tag)),
)),
}
}
} }
CommandAction::PreviousShell => { CommandAction::PreviousShell => {
context.shell_manager.prev(); context.shell_manager.prev();

View file

@ -512,7 +512,7 @@ pub trait PerItemCommand: Send + Sync {
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
registry: &CommandRegistry, registry: &CommandRegistry,
shell_manager: &ShellManager, raw_args: &RawCommandArgs,
input: Tagged<Value>, input: Tagged<Value>,
) -> Result<OutputStream, ShellError>; ) -> Result<OutputStream, ShellError>;
@ -579,7 +579,7 @@ impl Command {
.call_info .call_info
.evaluate(&registry, &Scope::it_value(x.clone())) .evaluate(&registry, &Scope::it_value(x.clone()))
.unwrap(); .unwrap();
match command.run(&call_info, &registry, &raw_args.shell_manager, x) { match command.run(&call_info, &registry, &raw_args, x) {
Ok(o) => o, Ok(o) => o,
Err(e) => VecDeque::from(vec![ReturnValue::Err(e)]).to_output_stream(), Err(e) => VecDeque::from(vec![ReturnValue::Err(e)]).to_output_stream(),
} }
@ -596,7 +596,10 @@ impl Command {
.unwrap(); .unwrap();
// We don't have an $it or block, so just execute what we have // We don't have an $it or block, so just execute what we have
match command.run(&call_info, &registry, &raw_args.shell_manager, nothing) { match command
.run(&call_info, &registry, &raw_args, nothing)
.into()
{
Ok(o) => o, Ok(o) => o,
Err(e) => OutputStream::one(Err(e)), Err(e) => OutputStream::one(Err(e)),
} }

View file

@ -19,10 +19,10 @@ impl PerItemCommand for Cpy {
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
_registry: &CommandRegistry, _registry: &CommandRegistry,
shell_manager: &ShellManager, raw_args: &RawCommandArgs,
_input: Tagged<Value>, _input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info.process(shell_manager, cp)?.run() call_info.process(&raw_args.shell_manager, cp)?.run()
} }
fn name(&self) -> &str { fn name(&self) -> &str {

View file

@ -1,8 +1,10 @@
use crate::commands::command::CommandAction; use crate::commands::command::CommandAction;
use crate::commands::PerItemCommand; use crate::commands::PerItemCommand;
use crate::commands::UnevaluatedCallInfo;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::registry; use crate::parser::registry;
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf;
pub struct Enter; pub struct Enter;
@ -18,18 +20,109 @@ impl PerItemCommand for Enter {
fn run( fn run(
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
_registry: &registry::CommandRegistry, registry: &registry::CommandRegistry,
_shell_manager: &ShellManager, raw_args: &RawCommandArgs,
_input: Tagged<Value>, _input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let raw_args = raw_args.clone();
match call_info.args.expect_nth(0)? { match call_info.args.expect_nth(0)? {
Tagged { Tagged {
item: Value::Primitive(Primitive::String(location)), item: Value::Primitive(Primitive::String(location)),
.. ..
} => Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( } => {
location.to_string(), let location = location.to_string();
)))] let location_clone = location.to_string();
.into()), if PathBuf::from(location).is_dir() {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
location_clone,
)))]
.into())
} else {
let stream = async_stream_block! {
// If it's a file, attempt to open the file as a value and enter it
let cwd = raw_args.shell_manager.path();
let full_path = std::path::PathBuf::from(cwd);
let (file_extension, contents, contents_tag, span_source) =
crate::commands::open::fetch(
&full_path,
&location_clone,
Span::unknown(),
)
.await.unwrap();
if let Some(uuid) = contents_tag.origin {
// If we have loaded something, track its source
yield ReturnSuccess::action(CommandAction::AddSpanSource(
uuid,
span_source,
));
}
match contents {
Value::Primitive(Primitive::String(_)) => {
let tagged_contents = contents.tagged(contents_tag);
if let Some(extension) = file_extension {
let command_name = format!("from-{}", extension);
if let Some(converter) =
registry.get_command(&command_name)
{
let new_args = RawCommandArgs {
host: raw_args.host,
shell_manager: raw_args.shell_manager,
call_info: UnevaluatedCallInfo {
args: crate::parser::hir::Call {
head: raw_args.call_info.args.head,
positional: None,
named: None,
},
source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map,
name_span: raw_args.call_info.name_span,
},
};
let mut result = converter.run(
new_args.with_input(vec![tagged_contents]),
&registry,
);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await;
for res in result_vec {
match res {
Ok(ReturnSuccess::Value(Tagged {
item,
..
})) => {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
Tagged {
item: item,
tag: contents_tag,
})));
}
x => yield x,
}
}
} else {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
}
} else {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
}
}
_ => {
let tagged_contents = contents.tagged(contents_tag);
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
}
}
};
Ok(stream.to_output_stream())
}
}
x => Ok( x => Ok(
vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell( vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
x.clone(), x.clone(),

View file

@ -27,6 +27,26 @@ impl WholeStreamCommand for FromSQLite {
} }
} }
pub struct FromDB;
impl WholeStreamCommand for FromDB {
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
from_sqlite(args, registry)
}
fn name(&self) -> &str {
"from-db"
}
fn signature(&self) -> Signature {
Signature::build("from-db")
}
}
pub fn convert_sqlite_file_to_nu_value( pub fn convert_sqlite_file_to_nu_value(
path: &Path, path: &Path,
tag: impl Into<Tag> + Clone, tag: impl Into<Tag> + Clone,

View file

@ -23,6 +23,26 @@ impl WholeStreamCommand for FromYAML {
} }
} }
pub struct FromYML;
impl WholeStreamCommand for FromYML {
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
from_yaml(args, registry)
}
fn name(&self) -> &str {
"from-yml"
}
fn signature(&self) -> Signature {
Signature::build("from-yml")
}
}
fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into<Tag>) -> Tagged<Value> { fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into<Tag>) -> Tagged<Value> {
let tag = tag.into(); let tag = tag.into();

View file

@ -16,10 +16,10 @@ impl PerItemCommand for Mkdir {
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
_registry: &CommandRegistry, _registry: &CommandRegistry,
shell_manager: &ShellManager, raw_args: &RawCommandArgs,
_input: Tagged<Value>, _input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info.process(shell_manager, mkdir)?.run() call_info.process(&raw_args.shell_manager, mkdir)?.run()
} }
fn name(&self) -> &str { fn name(&self) -> &str {

View file

@ -29,10 +29,10 @@ impl PerItemCommand for Move {
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
_registry: &CommandRegistry, _registry: &CommandRegistry,
shell_manager: &ShellManager, raw_args: &RawCommandArgs,
_input: Tagged<Value>, _input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info.process(shell_manager, mv)?.run() call_info.process(&raw_args.shell_manager, mv)?.run()
} }
} }

View file

@ -1,6 +1,7 @@
use crate::commands::UnevaluatedCallInfo;
use crate::context::SpanSource; use crate::context::SpanSource;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{Primitive, Value}; use crate::object::Value;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxType;
use crate::parser::registry::Signature; use crate::parser::registry::Signature;
use crate::prelude::*; use crate::prelude::*;
@ -25,15 +26,20 @@ impl PerItemCommand for Open {
fn run( fn run(
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
_registry: &CommandRegistry, registry: &CommandRegistry,
shell_manager: &ShellManager, raw_args: &RawCommandArgs,
_input: Tagged<Value>, _input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
run(call_info, shell_manager) run(call_info, registry, raw_args)
} }
} }
fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStream, ShellError> { fn run(
call_info: &CallInfo,
registry: &CommandRegistry,
raw_args: &RawCommandArgs,
) -> Result<OutputStream, ShellError> {
let shell_manager = &raw_args.shell_manager;
let cwd = PathBuf::from(shell_manager.path()); let cwd = PathBuf::from(shell_manager.path());
let full_path = PathBuf::from(cwd); let full_path = PathBuf::from(cwd);
@ -47,8 +53,9 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStrea
let path_buf = path.as_path()?; let path_buf = path.as_path()?;
let path_str = path_buf.display().to_string(); let path_str = path_buf.display().to_string();
let path_span = path.span(); let path_span = path.span();
let name_span = call_info.name_span;
let has_raw = call_info.args.has("raw"); let has_raw = call_info.args.has("raw");
let registry = registry.clone();
let raw_args = raw_args.clone();
let stream = async_stream_block! { let stream = async_stream_block! {
@ -65,7 +72,6 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStrea
file_extension.or(path_str.split('.').last().map(String::from)) file_extension.or(path_str.split('.').last().map(String::from))
}; };
if let Some(uuid) = contents_tag.origin { if let Some(uuid) = contents_tag.origin {
// If we have loaded something, track its source // If we have loaded something, track its source
yield ReturnSuccess::action(CommandAction::AddSpanSource( yield ReturnSuccess::action(CommandAction::AddSpanSource(
@ -74,39 +80,46 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result<OutputStrea
)); ));
} }
match contents { let tagged_contents = contents.tagged(contents_tag);
Value::Primitive(Primitive::String(string)) => {
let value = parse_string_as_value(file_extension, string, contents_tag, name_span).unwrap();
match value { if let Some(extension) = file_extension {
Tagged { let command_name = format!("from-{}", extension);
item: Value::List(list), if let Some(converter) = registry.get_command(&command_name) {
.. let new_args = RawCommandArgs {
} => { host: raw_args.host,
for elem in list { shell_manager: raw_args.shell_manager,
yield ReturnSuccess::value(elem); call_info: UnevaluatedCallInfo {
} args: crate::parser::hir::Call {
head: raw_args.call_info.args.head,
positional: None,
named: None
},
source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map,
name_span: raw_args.call_info.name_span,
} }
x => yield ReturnSuccess::value(x), };
} let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &registry);
} let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
Value::Binary(binary) => { for res in result_vec {
let value = parse_binary_as_value(file_extension, binary, contents_tag, name_span).unwrap(); match res {
Ok(ReturnSuccess::Value(Tagged { item: Value::List(list), ..})) => {
match value { for l in list {
Tagged { yield Ok(ReturnSuccess::Value(l));
item: Value::List(list), }
..
} => {
for elem in list {
yield ReturnSuccess::value(elem);
} }
Ok(ReturnSuccess::Value(Tagged { item, .. })) => {
yield Ok(ReturnSuccess::Value(Tagged { item: item, tag: contents_tag }));
}
x => yield x,
} }
x => yield ReturnSuccess::value(x),
} }
} else {
yield ReturnSuccess::value(tagged_contents);
} }
other => yield ReturnSuccess::value(other.tagged(contents_tag)), } else {
}; yield ReturnSuccess::value(tagged_contents);
}
}; };
Ok(stream.to_output_stream()) Ok(stream.to_output_stream())
@ -419,124 +432,3 @@ fn read_be_u16(input: &[u8]) -> Option<Vec<u16>> {
Some(result) Some(result)
} }
} }
pub fn parse_string_as_value(
extension: Option<String>,
contents: String,
contents_tag: Tag,
name_span: Span,
) -> Result<Tagged<Value>, ShellError> {
match extension {
Some(ref x) if x == "csv" => {
crate::commands::from_csv::from_csv_string_to_value(contents, false, contents_tag)
.map_err(move |_| {
ShellError::labeled_error(
"Could not open as CSV",
"could not open as CSV",
name_span,
)
})
}
Some(ref x) if x == "tsv" => {
crate::commands::from_tsv::from_tsv_string_to_value(contents, false, contents_tag)
.map_err(move |_| {
ShellError::labeled_error(
"Could not open as TSV",
"could not open as TSV",
name_span,
)
})
}
Some(ref x) if x == "toml" => {
crate::commands::from_toml::from_toml_string_to_value(contents, contents_tag).map_err(
move |_| {
ShellError::labeled_error(
"Could not open as TOML",
"could not open as TOML",
name_span,
)
},
)
}
Some(ref x) if x == "json" => {
crate::commands::from_json::from_json_string_to_value(contents, contents_tag).map_err(
move |_| {
ShellError::labeled_error(
"Could not open as JSON",
"could not open as JSON",
name_span,
)
},
)
}
Some(ref x) if x == "ini" => crate::commands::from_ini::from_ini_string_to_value(
contents,
contents_tag,
)
.map_err(move |_| {
ShellError::labeled_error("Could not open as INI", "could not open as INI", name_span)
}),
Some(ref x) if x == "xml" => crate::commands::from_xml::from_xml_string_to_value(
contents,
contents_tag,
)
.map_err(move |_| {
ShellError::labeled_error("Could not open as XML", "could not open as XML", name_span)
}),
Some(ref x) if x == "yml" => {
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_tag).map_err(
move |_| {
ShellError::labeled_error(
"Could not open as YAML",
"could not open as YAML",
name_span,
)
},
)
}
Some(ref x) if x == "yaml" => {
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_tag).map_err(
move |_| {
ShellError::labeled_error(
"Could not open as YAML",
"could not open as YAML",
name_span,
)
},
)
}
_ => Ok(Value::string(contents).tagged(contents_tag)),
}
}
pub fn parse_binary_as_value(
extension: Option<String>,
contents: Vec<u8>,
contents_tag: Tag,
name_span: Span,
) -> Result<Tagged<Value>, ShellError> {
match extension {
Some(ref x) if x == "bson" => {
crate::commands::from_bson::from_bson_bytes_to_value(contents, contents_tag).map_err(
move |_| {
ShellError::labeled_error(
"Could not open as BSON",
"could not open as BSON",
name_span,
)
},
)
}
Some(ref x) if x == "db" => {
crate::commands::from_sqlite::from_sqlite_bytes_to_value(contents, contents_tag)
.map_err(move |_| {
ShellError::labeled_error(
"Could not open as SQLite",
"could not open as SQLite",
name_span,
)
})
}
_ => Ok(Value::Binary(contents).tagged(contents_tag)),
}
}

View file

@ -28,10 +28,10 @@ impl PerItemCommand for Remove {
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
_registry: &CommandRegistry, _registry: &CommandRegistry,
shell_manager: &ShellManager, raw_args: &RawCommandArgs,
_input: Tagged<Value>, _input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
call_info.process(shell_manager, rm)?.run() call_info.process(&raw_args.shell_manager, rm)?.run()
} }
} }

View file

@ -1,8 +1,4 @@
use crate::commands::to_csv::{to_string as to_csv_to_string, value_to_csv_value}; use crate::commands::UnevaluatedCallInfo;
use crate::commands::to_tsv::{to_string as to_tsv_to_string, value_to_tsv_value};
use crate::commands::to_json::value_to_json_value;
use crate::commands::to_toml::value_to_toml_value;
use crate::commands::to_yaml::value_to_yaml_value;
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::Value; use crate::object::Value;
@ -33,7 +29,7 @@ impl WholeStreamCommand for Save {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
args.process(registry, save)?.run() Ok(args.process_raw(registry, save)?.run())
} }
} }
@ -47,16 +43,19 @@ fn save(
name, name,
shell_manager, shell_manager,
source_map, source_map,
host,
commands: registry,
.. ..
}: RunnableContext, }: RunnableContext,
raw_args: RawCommandArgs,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let mut full_path = PathBuf::from(shell_manager.path()); let mut full_path = PathBuf::from(shell_manager.path());
let name_span = name; let name_span = name;
if path.is_none() { let source_map = source_map.clone();
let source_map = source_map.clone(); let stream = async_stream_block! {
let stream = async_stream_block! { let input: Vec<Tagged<Value>> = input.values.collect().await;
let input: Vec<Tagged<Value>> = input.values.collect().await; if path.is_none() {
// If there is no filename, check the metadata for the origin filename // If there is no filename, check the metadata for the origin filename
if input.len() > 0 { if input.len() > 0 {
let origin = input[0].origin(); let origin = input[0].origin();
@ -88,50 +87,99 @@ fn save(
name_span, name_span,
)); ));
} }
} else {
let content = if !save_raw { if let Some(file) = path {
to_string_for(full_path.extension(), &input) full_path.push(file.item());
} else {
string_from(&input)
};
match content {
Ok(save_data) => match std::fs::write(full_path, save_data) {
Ok(o) => o,
Err(e) => yield Err(ShellError::string(e.to_string())),
},
Err(e) => yield Err(ShellError::string(e.to_string())),
} }
};
Ok(OutputStream::new(stream))
} else {
if let Some(file) = path {
full_path.push(file.item());
} }
let stream = async_stream_block! { let content = if !save_raw {
let input: Vec<Tagged<Value>> = input.values.collect().await; if let Some(extension) = full_path.extension() {
let command_name = format!("to-{}", extension.to_str().unwrap());
let content = if !save_raw { if let Some(converter) = registry.get_command(&command_name) {
to_string_for(full_path.extension(), &input) let new_args = RawCommandArgs {
host: host,
shell_manager: shell_manager,
call_info: UnevaluatedCallInfo {
args: crate::parser::hir::Call {
head: raw_args.call_info.args.head,
positional: None,
named: None
},
source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map,
name_span: raw_args.call_info.name_span,
}
};
let mut result = converter.run(new_args.with_input(input), &registry);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
let mut result_string = String::new();
for res in result_vec {
match res {
Ok(ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::String(s)), .. })) => {
result_string.push_str(&s);
}
_ => {
yield Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during saveS",
name_span,
));
},
}
}
Ok(result_string)
} else {
let mut result_string = String::new();
for res in input {
match res {
Tagged { item: Value::Primitive(Primitive::String(s)), .. } => {
result_string.push_str(&s);
}
_ => {
yield Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during saveS",
name_span,
));
},
}
}
Ok(result_string)
}
} else { } else {
string_from(&input) let mut result_string = String::new();
}; for res in input {
match res {
match content { Tagged { item: Value::Primitive(Primitive::String(s)), .. } => {
Ok(save_data) => match std::fs::write(full_path, save_data) { result_string.push_str(&s);
Ok(o) => o, }
Err(e) => yield Err(ShellError::string(e.to_string())), _ => {
}, yield Err(ShellError::labeled_error(
Err(e) => yield Err(ShellError::string(e.to_string())), "Save could not successfully save",
"unexpected data during saveS",
name_span,
));
},
}
}
Ok(result_string)
} }
} else {
string_from(&input)
}; };
Ok(OutputStream::new(stream)) match content {
} Ok(save_data) => match std::fs::write(full_path, save_data) {
Ok(o) => o,
Err(e) => yield Err(ShellError::string(e.to_string())),
},
Err(e) => yield Err(ShellError::string(e.to_string())),
}
};
Ok(OutputStream::new(stream))
} }
fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> { fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> {
@ -153,66 +201,3 @@ fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> {
Ok(save_data) Ok(save_data)
} }
fn to_string_for(
ext: Option<&std::ffi::OsStr>,
input: &Vec<Tagged<Value>>,
) -> Result<String, ShellError> {
let contents = match ext {
Some(x) if x == "csv" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to csv requires a single object (or use --raw)",
));
}
to_csv_to_string(&value_to_csv_value(&input[0]))?
}
Some(x) if x == "tsv" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to tsv requires a single object (or use --raw)",
));
}
to_tsv_to_string(&value_to_tsv_value(&input[0]))?
}
Some(x) if x == "toml" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to toml requires a single object (or use --raw)",
));
}
toml::to_string(&value_to_toml_value(&input[0]))?
}
Some(x) if x == "json" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to json requires a single object (or use --raw)",
));
}
serde_json::to_string(&value_to_json_value(&input[0]))?
}
Some(x) if x == "yml" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to yml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0]))?
}
Some(x) if x == "yaml" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to yaml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0]))?
}
_ => {
return Err(ShellError::string(
"tried saving a single object with an unrecognized format.",
))
}
};
Ok(contents)
}

View file

@ -25,6 +25,26 @@ impl WholeStreamCommand for ToSQLite {
} }
} }
pub struct ToDB;
impl WholeStreamCommand for ToDB {
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
to_sqlite(args, registry)
}
fn name(&self) -> &str {
"to-db"
}
fn signature(&self) -> Signature {
Signature::build("to-db")
}
}
fn comma_concat(acc: String, current: String) -> String { fn comma_concat(acc: String, current: String) -> String {
if acc == "" { if acc == "" {
current current

View file

@ -19,7 +19,7 @@ impl PerItemCommand for Where {
&self, &self,
call_info: &CallInfo, call_info: &CallInfo,
_registry: &registry::CommandRegistry, _registry: &registry::CommandRegistry,
_shell_manager: &ShellManager, _raw_args: &RawCommandArgs,
input: Tagged<Value>, input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let input_clone = input.clone(); let input_clone = input.clone();

View file

@ -39,11 +39,11 @@ pub fn path(head: impl Into<Expression>, tail: Vec<Tagged<impl Into<String>>>) -
#[derive(Debug, Clone, Eq, PartialEq, Getters, Serialize, Deserialize, new)] #[derive(Debug, Clone, Eq, PartialEq, Getters, Serialize, Deserialize, new)]
pub struct Call { pub struct Call {
#[get = "crate"] #[get = "crate"]
head: Box<Expression>, pub head: Box<Expression>,
#[get = "crate"] #[get = "crate"]
positional: Option<Vec<Expression>>, pub positional: Option<Vec<Expression>>,
#[get = "crate"] #[get = "crate"]
named: Option<NamedArguments>, pub named: Option<NamedArguments>,
} }
impl Call { impl Call {

View file

@ -52,6 +52,7 @@ pub(crate) use crate::commands::command::{
CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext, CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext,
}; };
pub(crate) use crate::commands::PerItemCommand; pub(crate) use crate::commands::PerItemCommand;
pub(crate) use crate::commands::RawCommandArgs;
pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::CommandRegistry;
pub(crate) use crate::context::{Context, SpanSource}; pub(crate) use crate::context::{Context, SpanSource};
pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::host::handle_unexpected;