2019-06-07 16:30:50 +00:00
|
|
|
use crate::commands::autoview;
|
2019-06-07 06:34:42 +00:00
|
|
|
use crate::commands::classified::SinkCommand;
|
|
|
|
use crate::commands::command::sink;
|
|
|
|
|
2019-05-23 04:30:43 +00:00
|
|
|
use crate::prelude::*;
|
|
|
|
|
2019-05-24 07:29:16 +00:00
|
|
|
use crate::commands::classified::{
|
2019-05-26 06:54:41 +00:00
|
|
|
ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand,
|
|
|
|
StreamNext,
|
2019-05-24 07:29:16 +00:00
|
|
|
};
|
2019-05-23 04:30:43 +00:00
|
|
|
use crate::context::Context;
|
|
|
|
crate use crate::errors::ShellError;
|
2019-05-28 06:45:18 +00:00
|
|
|
use crate::evaluate::Scope;
|
2019-06-07 06:34:42 +00:00
|
|
|
|
2019-06-03 05:11:21 +00:00
|
|
|
use crate::git::current_branch;
|
2019-05-23 04:30:43 +00:00
|
|
|
use crate::object::Value;
|
2019-06-06 06:34:59 +00:00
|
|
|
use crate::parser::ast::{Expression, Leaf, RawExpression};
|
|
|
|
use crate::parser::{Args, Pipeline};
|
2019-05-23 04:30:43 +00:00
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
use log::debug;
|
2019-05-23 04:30:43 +00:00
|
|
|
use rustyline::error::ReadlineError;
|
|
|
|
use rustyline::{self, ColorMode, Config, Editor};
|
2019-06-07 06:34:42 +00:00
|
|
|
|
2019-05-23 04:30:43 +00:00
|
|
|
use std::error::Error;
|
2019-05-24 07:29:16 +00:00
|
|
|
use std::iter::Iterator;
|
2019-06-07 00:31:22 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2019-05-23 04:30:43 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum MaybeOwned<'a, T> {
|
|
|
|
Owned(T),
|
|
|
|
Borrowed(&'a T),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> MaybeOwned<'a, T> {
|
|
|
|
crate fn borrow(&self) -> &T {
|
|
|
|
match self {
|
|
|
|
MaybeOwned::Owned(v) => v,
|
|
|
|
MaybeOwned::Borrowed(v) => v,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 03:48:58 +00:00
|
|
|
pub async fn cli() -> Result<(), Box<dyn Error>> {
|
2019-05-23 04:30:43 +00:00
|
|
|
let mut context = Context::basic()?;
|
|
|
|
|
|
|
|
{
|
|
|
|
use crate::commands::*;
|
|
|
|
|
|
|
|
context.add_commands(vec![
|
2019-05-28 06:45:18 +00:00
|
|
|
command("ps", ps::ps),
|
|
|
|
command("ls", ls::ls),
|
|
|
|
command("cd", cd::cd),
|
|
|
|
command("view", view::view),
|
|
|
|
command("skip", skip::skip),
|
2019-06-02 18:53:30 +00:00
|
|
|
command("first", first::first),
|
2019-05-28 06:45:18 +00:00
|
|
|
command("size", size::size),
|
|
|
|
command("from-json", from_json::from_json),
|
2019-06-01 07:05:57 +00:00
|
|
|
command("from-toml", from_toml::from_toml),
|
2019-06-03 07:41:28 +00:00
|
|
|
command("from-yaml", from_yaml::from_yaml),
|
2019-06-05 01:53:38 +00:00
|
|
|
command("get", get::get),
|
2019-05-28 06:45:18 +00:00
|
|
|
command("open", open::open),
|
2019-06-02 18:53:30 +00:00
|
|
|
command("pick", pick::pick),
|
2019-05-31 20:34:15 +00:00
|
|
|
command("split-column", split_column::split_column),
|
|
|
|
command("split-row", split_row::split_row),
|
2019-05-28 06:45:18 +00:00
|
|
|
command("reject", reject::reject),
|
2019-06-01 03:43:59 +00:00
|
|
|
command("trim", trim::trim),
|
2019-05-28 06:45:18 +00:00
|
|
|
command("to-array", to_array::to_array),
|
|
|
|
command("to-json", to_json::to_json),
|
2019-06-01 18:26:04 +00:00
|
|
|
command("to-toml", to_toml::to_toml),
|
2019-05-28 06:45:18 +00:00
|
|
|
Arc::new(Where),
|
2019-06-01 05:50:16 +00:00
|
|
|
Arc::new(Config),
|
2019-05-28 06:45:18 +00:00
|
|
|
command("sort-by", sort_by::sort_by),
|
2019-05-23 04:30:43 +00:00
|
|
|
]);
|
2019-06-07 06:34:42 +00:00
|
|
|
|
2019-06-07 07:50:26 +00:00
|
|
|
context.add_sinks(vec![
|
|
|
|
sink("autoview", autoview::autoview),
|
2019-06-07 16:46:47 +00:00
|
|
|
sink("clip", clip::clip),
|
2019-06-07 17:13:38 +00:00
|
|
|
sink("save", save::save),
|
2019-06-07 07:50:26 +00:00
|
|
|
sink("tree", tree::tree),
|
|
|
|
]);
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
|
|
|
let h = crate::shell::Helper::new(context.clone_commands());
|
|
|
|
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let _ = ansi_term::enable_ansi_support();
|
|
|
|
}
|
|
|
|
|
|
|
|
rl.set_helper(Some(h));
|
2019-06-03 00:03:40 +00:00
|
|
|
let _ = rl.load_history("history.txt");
|
2019-05-26 06:54:41 +00:00
|
|
|
|
2019-06-07 00:31:22 +00:00
|
|
|
let ctrl_c = Arc::new(AtomicBool::new(false));
|
|
|
|
let cc = ctrl_c.clone();
|
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
cc.store(true, Ordering::SeqCst);
|
|
|
|
})
|
|
|
|
.expect("Error setting Ctrl-C handler");
|
|
|
|
|
2019-05-23 04:30:43 +00:00
|
|
|
loop {
|
2019-06-07 00:31:22 +00:00
|
|
|
if ctrl_c.load(Ordering::SeqCst) {
|
|
|
|
ctrl_c.store(false, Ordering::SeqCst);
|
|
|
|
if let ShellError::String(s) = ShellError::string("CTRL-C") {
|
|
|
|
context.host.lock().unwrap().stdout(&format!("{:?}", s));
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-05-23 07:23:06 +00:00
|
|
|
let readline = rl.readline(&format!(
|
2019-06-01 21:11:28 +00:00
|
|
|
"{}{}> ",
|
2019-06-09 17:52:56 +00:00
|
|
|
context.env.lock().unwrap().cwd().display(),
|
2019-06-01 21:11:28 +00:00
|
|
|
match current_branch() {
|
|
|
|
Some(s) => format!("({})", s),
|
2019-06-03 05:11:21 +00:00
|
|
|
None => "".to_string(),
|
2019-06-01 21:11:28 +00:00
|
|
|
}
|
2019-05-23 07:23:06 +00:00
|
|
|
));
|
2019-05-23 04:30:43 +00:00
|
|
|
|
2019-05-23 07:23:06 +00:00
|
|
|
match process_line(readline, &mut context).await {
|
2019-05-23 04:30:43 +00:00
|
|
|
LineResult::Success(line) => {
|
|
|
|
rl.add_history_entry(line.clone());
|
|
|
|
}
|
|
|
|
|
2019-06-07 22:35:07 +00:00
|
|
|
LineResult::Error(mut line, err) => match err {
|
|
|
|
ShellError::Diagnostic(diag) => {
|
2019-05-30 04:19:46 +00:00
|
|
|
let host = context.host.lock().unwrap();
|
|
|
|
let writer = host.err_termcolor();
|
2019-06-07 22:35:07 +00:00
|
|
|
line.push_str(" ");
|
|
|
|
let files = crate::parser::span::Files::new(line);
|
2019-05-30 04:19:46 +00:00
|
|
|
|
|
|
|
language_reporting::emit(
|
|
|
|
&mut writer.lock(),
|
|
|
|
&files,
|
|
|
|
&diag.diagnostic,
|
|
|
|
&language_reporting::DefaultConfig,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
2019-06-03 05:11:21 +00:00
|
|
|
ShellError::TypeError(desc) => context
|
|
|
|
.host
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.stdout(&format!("TypeError: {}", desc)),
|
|
|
|
|
|
|
|
ShellError::MissingProperty { subpath, .. } => context
|
|
|
|
.host
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.stdout(&format!("Missing property {}", subpath)),
|
|
|
|
|
2019-06-07 22:35:07 +00:00
|
|
|
ShellError::String(_) => context.host.lock().unwrap().stdout(&format!("{}", err)),
|
2019-05-30 04:19:46 +00:00
|
|
|
},
|
2019-05-23 04:30:43 +00:00
|
|
|
|
|
|
|
LineResult::Break => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
LineResult::FatalError(err) => {
|
|
|
|
context
|
|
|
|
.host
|
2019-05-23 07:23:06 +00:00
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2019-05-23 04:30:43 +00:00
|
|
|
.stdout(&format!("A surprising fatal error occurred.\n{:?}", err));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rl.save_history("history.txt").unwrap();
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
enum LineResult {
|
|
|
|
Success(String),
|
2019-06-07 22:35:07 +00:00
|
|
|
Error(String, ShellError),
|
2019-05-23 04:30:43 +00:00
|
|
|
Break,
|
|
|
|
|
|
|
|
#[allow(unused)]
|
|
|
|
FatalError(ShellError),
|
|
|
|
}
|
|
|
|
|
2019-05-24 04:34:43 +00:00
|
|
|
impl std::ops::Try for LineResult {
|
|
|
|
type Ok = Option<String>;
|
|
|
|
type Error = ShellError;
|
|
|
|
|
|
|
|
fn into_result(self) -> Result<Option<String>, ShellError> {
|
|
|
|
match self {
|
|
|
|
LineResult::Success(s) => Ok(Some(s)),
|
2019-06-07 22:35:07 +00:00
|
|
|
LineResult::Error(_, s) => Err(s),
|
2019-05-24 04:34:43 +00:00
|
|
|
LineResult::Break => Ok(None),
|
|
|
|
LineResult::FatalError(err) => Err(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn from_error(v: ShellError) -> Self {
|
2019-06-07 22:35:07 +00:00
|
|
|
LineResult::Error(String::new(), v)
|
2019-05-24 04:34:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn from_ok(v: Option<String>) -> Self {
|
|
|
|
match v {
|
|
|
|
None => LineResult::Break,
|
|
|
|
Some(v) => LineResult::Success(v),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 07:23:06 +00:00
|
|
|
async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> LineResult {
|
2019-05-23 04:30:43 +00:00
|
|
|
match &readline {
|
|
|
|
Ok(line) if line.trim() == "exit" => LineResult::Break,
|
|
|
|
|
|
|
|
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
|
|
|
|
|
|
|
|
Ok(line) => {
|
2019-06-02 16:28:40 +00:00
|
|
|
let result = match crate::parser::parse(&line) {
|
2019-05-23 04:30:43 +00:00
|
|
|
Err(err) => {
|
2019-06-09 17:52:56 +00:00
|
|
|
return LineResult::Error(line.clone(), err);
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(val) => val,
|
|
|
|
};
|
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
debug!("=== Parsed ===");
|
|
|
|
debug!("{:#?}", result);
|
2019-05-24 07:29:16 +00:00
|
|
|
|
2019-06-07 06:34:42 +00:00
|
|
|
let mut pipeline = classify_pipeline(&result, ctx)?;
|
|
|
|
|
|
|
|
match pipeline.commands.last() {
|
|
|
|
Some(ClassifiedCommand::Sink(_)) => {}
|
2019-06-07 07:54:52 +00:00
|
|
|
Some(ClassifiedCommand::External(_)) => {}
|
2019-06-07 06:34:42 +00:00
|
|
|
_ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand {
|
|
|
|
command: sink("autoview", autoview::autoview),
|
2019-06-07 22:35:07 +00:00
|
|
|
name_span: None,
|
2019-06-07 06:34:42 +00:00
|
|
|
args: Args {
|
|
|
|
positional: vec![],
|
|
|
|
named: indexmap::IndexMap::new(),
|
|
|
|
},
|
|
|
|
})),
|
|
|
|
}
|
2019-05-24 07:29:16 +00:00
|
|
|
|
|
|
|
let mut input = ClassifiedInputStream::new();
|
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
let mut iter = pipeline.commands.into_iter().peekable();
|
2019-05-24 07:29:16 +00:00
|
|
|
|
|
|
|
loop {
|
|
|
|
let item: Option<ClassifiedCommand> = iter.next();
|
|
|
|
let next: Option<&ClassifiedCommand> = iter.peek();
|
|
|
|
|
|
|
|
input = match (item, next) {
|
|
|
|
(None, _) => break,
|
|
|
|
|
2019-06-04 21:42:31 +00:00
|
|
|
(Some(ClassifiedCommand::Expr(_)), _) => {
|
2019-06-09 17:52:56 +00:00
|
|
|
return LineResult::Error(line.clone(), ShellError::unimplemented(
|
2019-06-04 21:42:31 +00:00
|
|
|
"Expression-only commands",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
(_, Some(ClassifiedCommand::Expr(_))) => {
|
2019-06-09 17:52:56 +00:00
|
|
|
return LineResult::Error(line.clone(), ShellError::unimplemented(
|
2019-06-04 21:42:31 +00:00
|
|
|
"Expression-only commands",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2019-06-07 06:34:42 +00:00
|
|
|
(Some(ClassifiedCommand::Sink(_)), Some(_)) => {
|
2019-06-09 17:52:56 +00:00
|
|
|
return LineResult::Error(line.clone(), ShellError::string("Commands like table, save, and autoview must come last in the pipeline"))
|
2019-06-07 06:34:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(Some(ClassifiedCommand::Sink(left)), None) => {
|
|
|
|
let input_vec: Vec<Value> = input.objects.collect().await;
|
|
|
|
left.run(
|
|
|
|
ctx,
|
|
|
|
input_vec,
|
|
|
|
)?;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-05-24 07:29:16 +00:00
|
|
|
(
|
|
|
|
Some(ClassifiedCommand::Internal(left)),
|
2019-06-07 06:34:42 +00:00
|
|
|
Some(ClassifiedCommand::External(_)),
|
|
|
|
) => match left.run(ctx, input).await {
|
|
|
|
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-06-07 06:34:42 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
(
|
|
|
|
Some(ClassifiedCommand::Internal(left)),
|
|
|
|
Some(_),
|
2019-05-24 07:29:16 +00:00
|
|
|
) => match left.run(ctx, input).await {
|
|
|
|
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 07:29:16 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
(Some(ClassifiedCommand::Internal(left)), None) => {
|
|
|
|
match left.run(ctx, input).await {
|
|
|
|
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 07:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(
|
|
|
|
Some(ClassifiedCommand::External(left)),
|
|
|
|
Some(ClassifiedCommand::External(_)),
|
2019-05-24 18:48:33 +00:00
|
|
|
) => match left.run(ctx, input, StreamNext::External).await {
|
2019-05-24 07:29:16 +00:00
|
|
|
Ok(val) => val,
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 07:29:16 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
(
|
2019-05-24 18:48:33 +00:00
|
|
|
Some(ClassifiedCommand::External(left)),
|
2019-06-07 06:34:42 +00:00
|
|
|
Some(_),
|
2019-05-24 18:48:33 +00:00
|
|
|
) => match left.run(ctx, input, StreamNext::Internal).await {
|
|
|
|
Ok(val) => val,
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 18:48:33 +00:00
|
|
|
},
|
2019-05-24 07:29:16 +00:00
|
|
|
|
|
|
|
(Some(ClassifiedCommand::External(left)), None) => {
|
2019-05-24 18:48:33 +00:00
|
|
|
match left.run(ctx, input, StreamNext::Last).await {
|
2019-05-24 07:29:16 +00:00
|
|
|
Ok(val) => val,
|
2019-06-09 17:52:56 +00:00
|
|
|
Err(err) => return LineResult::Error(line.clone(), err),
|
2019-05-24 07:29:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
|
2019-06-09 17:52:56 +00:00
|
|
|
LineResult::Success(line.clone())
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
2019-06-07 22:35:07 +00:00
|
|
|
Err(ReadlineError::Interrupted) => {
|
|
|
|
LineResult::Error("".to_string(), ShellError::string("CTRL-C"))
|
|
|
|
}
|
2019-05-23 04:30:43 +00:00
|
|
|
Err(ReadlineError::Eof) => {
|
|
|
|
println!("CTRL-D");
|
|
|
|
LineResult::Break
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
println!("Error: {:?}", err);
|
|
|
|
LineResult::Break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 06:54:41 +00:00
|
|
|
fn classify_pipeline(
|
|
|
|
pipeline: &Pipeline,
|
|
|
|
context: &Context,
|
|
|
|
) -> Result<ClassifiedPipeline, ShellError> {
|
|
|
|
let commands: Result<Vec<_>, _> = pipeline
|
|
|
|
.commands
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.map(|item| classify_command(&item, context))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(ClassifiedPipeline {
|
|
|
|
commands: commands?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-05-23 04:30:43 +00:00
|
|
|
fn classify_command(
|
2019-06-04 21:42:31 +00:00
|
|
|
command: &Expression,
|
2019-05-23 04:30:43 +00:00
|
|
|
context: &Context,
|
|
|
|
) -> Result<ClassifiedCommand, ShellError> {
|
2019-06-04 21:42:31 +00:00
|
|
|
// let command_name = &command.name[..];
|
|
|
|
// let args = &command.args;
|
|
|
|
|
2019-06-06 06:34:59 +00:00
|
|
|
if let Expression {
|
|
|
|
expr: RawExpression::Call(call),
|
|
|
|
..
|
|
|
|
} = command
|
|
|
|
{
|
2019-06-04 21:42:31 +00:00
|
|
|
match (&call.name, &call.args) {
|
2019-06-06 06:34:59 +00:00
|
|
|
(
|
|
|
|
Expression {
|
|
|
|
expr: RawExpression::Leaf(Leaf::Bare(name)),
|
2019-06-07 22:35:07 +00:00
|
|
|
span,
|
2019-06-06 06:34:59 +00:00
|
|
|
},
|
|
|
|
args,
|
|
|
|
) => match context.has_command(&name.to_string()) {
|
|
|
|
true => {
|
|
|
|
let command = context.get_command(&name.to_string());
|
|
|
|
let config = command.config();
|
|
|
|
let scope = Scope::empty();
|
|
|
|
|
|
|
|
let args = match args {
|
|
|
|
Some(args) => config.evaluate_args(args.iter(), &scope)?,
|
|
|
|
None => Args::default(),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(ClassifiedCommand::Internal(InternalCommand {
|
|
|
|
command,
|
2019-06-07 22:35:07 +00:00
|
|
|
name_span: Some(span.clone()),
|
2019-06-06 06:34:59 +00:00
|
|
|
args,
|
|
|
|
}))
|
2019-06-04 21:42:31 +00:00
|
|
|
}
|
2019-06-07 06:34:42 +00:00
|
|
|
false => match context.has_sink(&name.to_string()) {
|
|
|
|
true => {
|
|
|
|
let command = context.get_sink(&name.to_string());
|
|
|
|
let config = command.config();
|
|
|
|
let scope = Scope::empty();
|
|
|
|
|
|
|
|
let args = match args {
|
|
|
|
Some(args) => config.evaluate_args(args.iter(), &scope)?,
|
|
|
|
None => Args::default(),
|
|
|
|
};
|
|
|
|
|
2019-06-07 22:35:07 +00:00
|
|
|
Ok(ClassifiedCommand::Sink(SinkCommand {
|
|
|
|
command,
|
|
|
|
name_span: Some(span.clone()),
|
|
|
|
args,
|
|
|
|
}))
|
2019-06-07 06:34:42 +00:00
|
|
|
}
|
|
|
|
false => {
|
|
|
|
let arg_list_strings: Vec<String> = match args {
|
|
|
|
Some(args) => args.iter().map(|i| i.as_external_arg()).collect(),
|
|
|
|
None => vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(ClassifiedCommand::External(ExternalCommand {
|
|
|
|
name: name.to_string(),
|
|
|
|
args: arg_list_strings,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
},
|
2019-06-06 06:34:59 +00:00
|
|
|
},
|
2019-06-04 21:42:31 +00:00
|
|
|
|
|
|
|
(_, None) => Err(ShellError::string(
|
|
|
|
"Unimplemented command that is just an expression (1)",
|
|
|
|
)),
|
2019-06-06 06:34:59 +00:00
|
|
|
(_, Some(_)) => Err(ShellError::string("Unimplemented dynamic command")),
|
2019-06-04 21:42:31 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(ShellError::string(&format!(
|
|
|
|
"Unimplemented command that is just an expression (2) -- {:?}",
|
|
|
|
command
|
|
|
|
)))
|
2019-05-23 04:30:43 +00:00
|
|
|
}
|
|
|
|
}
|