nushell/src/cli.rs

390 lines
12 KiB
Rust
Raw Normal View History

2019-05-23 06:30:43 +02:00
use crate::prelude::*;
2019-05-24 09:29:16 +02:00
use crate::commands::classified::{
2019-05-26 08:54:41 +02:00
ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand,
StreamNext,
2019-05-24 09:29:16 +02:00
};
2019-05-23 06:30:43 +02:00
use crate::context::Context;
crate use crate::errors::ShellError;
2019-05-28 08:45:18 +02:00
use crate::evaluate::Scope;
2019-05-23 06:30:43 +02:00
crate use crate::format::{EntriesListView, GenericView};
use crate::object::Value;
2019-05-26 08:54:41 +02:00
use crate::parser::{ParsedCommand, Pipeline};
use crate::stream::empty_stream;
2019-06-01 23:11:28 +02:00
use crate::git::current_branch;
2019-05-23 06:30:43 +02:00
2019-05-26 08:54:41 +02:00
use log::debug;
2019-05-23 06:30:43 +02:00
use rustyline::error::ReadlineError;
use rustyline::{self, ColorMode, Config, Editor};
use std::collections::VecDeque;
use std::error::Error;
2019-05-24 09:29:16 +02:00
use std::iter::Iterator;
2019-05-23 06:30:43 +02: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,
}
}
}
pub async fn cli() -> Result<(), Box<Error>> {
2019-05-23 06:30:43 +02:00
let mut context = Context::basic()?;
{
use crate::commands::*;
context.add_commands(vec![
2019-05-28 08:45:18 +02:00
command("format-list", format_list),
command("ps", ps::ps),
command("ls", ls::ls),
command("cd", cd::cd),
command("view", view::view),
command("skip", skip::skip),
command("first", take::take),
command("size", size::size),
command("from-json", from_json::from_json),
2019-06-01 09:05:57 +02:00
command("from-toml", from_toml::from_toml),
2019-05-28 08:45:18 +02:00
command("open", open::open),
command("column", column::column),
2019-05-31 22:34:15 +02:00
command("split-column", split_column::split_column),
command("split-row", split_row::split_row),
2019-05-28 08:45:18 +02:00
command("reject", reject::reject),
2019-05-29 06:02:36 +02:00
command("select", select::select),
2019-06-01 05:43:59 +02:00
command("trim", trim::trim),
2019-05-28 08:45:18 +02:00
command("to-array", to_array::to_array),
command("to-json", to_json::to_json),
2019-06-01 20:26:04 +02:00
command("to-toml", to_toml::to_toml),
2019-05-28 08:45:18 +02:00
Arc::new(Where),
Arc::new(Config),
2019-05-28 08:45:18 +02:00
command("sort-by", sort_by::sort_by),
2019-05-23 06:30:43 +02:00
]);
}
2019-05-26 08:54:41 +02: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));
if rl.load_history("history.txt").is_err() {
println!("No previous history.");
}
2019-05-23 06:30:43 +02:00
loop {
let readline = rl.readline(&format!(
2019-06-01 23:11:28 +02:00
"{}{}> ",
context.env.lock().unwrap().cwd().display().to_string(),
match current_branch() {
Some(s) => format!("({})", s),
None => "".to_string()
}
));
2019-05-23 06:30:43 +02:00
match process_line(readline, &mut context).await {
2019-05-23 06:30:43 +02:00
LineResult::Success(line) => {
rl.add_history_entry(line.clone());
}
LineResult::Error(err) => match err {
ShellError::Diagnostic(diag, source) => {
let host = context.host.lock().unwrap();
let writer = host.err_termcolor();
let files = crate::parser::span::Files::new(source);
language_reporting::emit(
&mut writer.lock(),
&files,
&diag.diagnostic,
&language_reporting::DefaultConfig,
)
.unwrap();
}
ShellError::String(s) => context.host.lock().unwrap().stdout(&format!("{:?}", s)),
},
2019-05-23 06:30:43 +02:00
LineResult::Break => {
break;
}
LineResult::FatalError(err) => {
context
.host
.lock()
.unwrap()
2019-05-23 06:30:43 +02:00
.stdout(&format!("A surprising fatal error occurred.\n{:?}", err));
}
}
}
rl.save_history("history.txt").unwrap();
Ok(())
}
enum LineResult {
Success(String),
Error(ShellError),
2019-05-23 06:30:43 +02:00
Break,
#[allow(unused)]
FatalError(ShellError),
}
2019-05-24 06:34:43 +02: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)),
LineResult::Error(s) => Err(s),
2019-05-24 06:34:43 +02:00
LineResult::Break => Ok(None),
LineResult::FatalError(err) => Err(err),
}
}
fn from_error(v: ShellError) -> Self {
LineResult::Error(v)
2019-05-24 06:34:43 +02:00
}
fn from_ok(v: Option<String>) -> Self {
match v {
None => LineResult::Break,
Some(v) => LineResult::Success(v),
}
}
}
async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> LineResult {
2019-05-23 06:30:43 +02: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 18:28:40 +02:00
let result = match crate::parser::parse(&line) {
2019-05-23 06:30:43 +02:00
Err(err) => {
return LineResult::Error(err);
2019-05-23 06:30:43 +02:00
}
Ok(val) => val,
};
2019-05-26 08:54:41 +02:00
debug!("=== Parsed ===");
debug!("{:#?}", result);
2019-05-24 09:29:16 +02:00
2019-05-26 08:54:41 +02:00
let pipeline = classify_pipeline(&result, ctx)?;
2019-05-24 09:29:16 +02:00
let mut input = ClassifiedInputStream::new();
2019-05-26 08:54:41 +02:00
let mut iter = pipeline.commands.into_iter().peekable();
2019-05-24 09:29:16 +02:00
loop {
let item: Option<ClassifiedCommand> = iter.next();
let next: Option<&ClassifiedCommand> = iter.peek();
input = match (item, next) {
(None, _) => break,
(
Some(ClassifiedCommand::Internal(left)),
Some(ClassifiedCommand::Internal(_)),
) => match left.run(ctx, input).await {
Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(err),
2019-05-24 09:29:16 +02:00
},
(Some(ClassifiedCommand::Internal(left)), None) => {
match left.run(ctx, input).await {
Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(err),
2019-05-24 09:29:16 +02:00
}
}
(
Some(ClassifiedCommand::External(left)),
Some(ClassifiedCommand::External(_)),
) => match left.run(ctx, input, StreamNext::External).await {
2019-05-24 09:29:16 +02:00
Ok(val) => val,
Err(err) => return LineResult::Error(err),
2019-05-24 09:29:16 +02:00
},
(
Some(ClassifiedCommand::Internal(left)),
Some(ClassifiedCommand::External(_)),
) => match left.run(ctx, input).await {
Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(err),
2019-06-02 18:28:40 +02:00
},
2019-05-24 09:29:16 +02:00
(
Some(ClassifiedCommand::External(left)),
2019-05-24 09:29:16 +02:00
Some(ClassifiedCommand::Internal(_)),
) => match left.run(ctx, input, StreamNext::Internal).await {
Ok(val) => val,
Err(err) => return LineResult::Error(err),
},
2019-05-24 09:29:16 +02:00
(Some(ClassifiedCommand::External(left)), None) => {
match left.run(ctx, input, StreamNext::Last).await {
2019-05-24 09:29:16 +02:00
Ok(val) => val,
Err(err) => return LineResult::Error(err),
2019-05-24 09:29:16 +02:00
}
}
}
2019-05-23 06:30:43 +02:00
}
2019-05-24 09:29:16 +02:00
let input_vec: VecDeque<_> = input.objects.collect().await;
if input_vec.len() > 0 {
if equal_shapes(&input_vec) {
let array = crate::commands::stream_to_array(input_vec.boxed()).await;
2019-05-23 06:30:43 +02:00
let args = CommandArgs::from_context(ctx, vec![], array);
2019-05-24 06:34:43 +02:00
let mut result = format(args);
let mut vec = vec![];
vec.send_all(&mut result).await?;
2019-05-23 06:30:43 +02:00
} else {
let args = CommandArgs::from_context(ctx, vec![], input_vec.boxed());
2019-05-24 06:34:43 +02:00
format(args).collect::<Vec<_>>().await;
2019-05-23 06:30:43 +02:00
}
}
LineResult::Success(line.to_string())
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
LineResult::Break
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
LineResult::Break
}
Err(err) => {
println!("Error: {:?}", err);
LineResult::Break
}
}
}
2019-05-26 08:54:41 +02: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 06:30:43 +02:00
fn classify_command(
2019-05-26 08:54:41 +02:00
command: &ParsedCommand,
2019-05-23 06:30:43 +02:00
context: &Context,
) -> Result<ClassifiedCommand, ShellError> {
2019-05-26 08:54:41 +02:00
let command_name = &command.name[..];
let args = &command.args;
2019-05-23 06:30:43 +02:00
2019-05-26 08:54:41 +02:00
match command_name {
other => match context.has_command(command_name) {
2019-05-23 06:30:43 +02:00
true => {
let command = context.get_command(command_name);
2019-05-28 08:45:18 +02:00
let config = command.config();
let scope = Scope::empty();
let args = config.evaluate_args(args.iter(), &scope)?;
2019-05-23 06:30:43 +02:00
Ok(ClassifiedCommand::Internal(InternalCommand {
command,
2019-05-28 08:45:18 +02:00
args,
}))
}
false => {
2019-05-30 07:33:47 +02:00
let arg_list_strings: Vec<String> =
args.iter().map(|i| i.as_external_arg()).collect();
2019-05-28 08:45:18 +02:00
Ok(ClassifiedCommand::External(ExternalCommand {
name: other.to_string(),
args: arg_list_strings,
2019-05-23 06:30:43 +02:00
}))
}
},
}
}
2019-05-24 06:34:43 +02:00
crate fn format(args: CommandArgs) -> OutputStream {
let host = args.host.clone();
let input = args.input.map(|a| a.copy());
let input = input.collect::<Vec<_>>();
input
.then(move |input| {
let last = input.len() - 1;
let mut host = host.lock().unwrap();
for (i, item) in input.iter().enumerate() {
let view = GenericView::new(item);
2019-05-24 21:35:22 +02:00
handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host));
2019-05-24 06:34:43 +02:00
if last != i {
2019-05-24 21:35:22 +02:00
host.stdout("");
2019-05-24 06:34:43 +02:00
}
}
2019-05-23 06:30:43 +02:00
2019-05-24 06:34:43 +02:00
futures::future::ready(empty_stream())
})
.flatten_stream()
.boxed()
2019-05-23 06:30:43 +02:00
}
2019-05-24 06:34:43 +02:00
crate fn format_list(args: CommandArgs) -> Result<OutputStream, ShellError> {
let host = args.host.clone();
let view = EntriesListView::from_stream(args.input);
Ok(view
.then(move |view| {
2019-05-24 21:35:22 +02:00
handle_unexpected(&mut *host.lock().unwrap(), |host| {
crate::format::print_view(&view, host)
});
2019-05-24 06:34:43 +02:00
futures::future::ready(empty_stream())
})
.flatten_stream()
.boxed())
2019-05-23 06:30:43 +02:00
}
fn equal_shapes(input: &VecDeque<Value>) -> bool {
let mut items = input.iter();
let item = match items.next() {
Some(item) => item,
None => return false,
};
let desc = item.data_descriptors();
for item in items {
if desc != item.data_descriptors() {
return false;
}
}
true
}