nushell/src/main.rs

257 lines
6.6 KiB
Rust
Raw Normal View History

2019-05-10 18:59:12 +02:00
#![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)]
2019-05-16 02:21:46 +02:00
#[allow(unused)]
use crate::prelude::*;
2019-05-10 18:59:12 +02:00
mod commands;
2019-05-15 18:12:38 +02:00
mod context;
2019-05-10 18:59:12 +02:00
mod env;
mod errors;
mod format;
mod object;
2019-05-11 06:45:57 +02:00
mod parser;
mod prelude;
2019-05-16 23:43:36 +02:00
mod shell;
2019-05-10 18:59:12 +02:00
use crate::commands::command::ReturnValue;
crate use crate::commands::command::{Command, CommandAction, CommandBlueprint};
2019-05-15 18:12:38 +02:00
use crate::context::Context;
2019-05-10 18:59:12 +02:00
crate use crate::env::{Environment, Host};
2019-05-11 06:45:57 +02:00
crate use crate::errors::ShellError;
2019-05-16 00:58:44 +02:00
crate use crate::format::{EntriesListView, GenericView};
2019-05-16 00:23:36 +02:00
use crate::object::Value;
2019-05-12 00:59:57 +02:00
2019-05-10 18:59:12 +02:00
use rustyline::error::ReadlineError;
2019-05-16 23:43:36 +02:00
use rustyline::{self, ColorMode, Config, Editor};
use std::collections::VecDeque;
2019-05-10 18:59:12 +02:00
use std::error::Error;
2019-05-15 18:12:38 +02:00
use std::sync::{Arc, Mutex};
2019-05-11 09:00:33 +02:00
use subprocess::Exec;
2019-05-10 18:59:12 +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,
}
}
}
fn main() -> Result<(), Box<Error>> {
let config = Config::builder().color_mode(ColorMode::Forced).build();
2019-05-16 23:43:36 +02:00
let h = crate::shell::Helper::new();
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
rl.set_helper(Some(h));
2019-05-10 18:59:12 +02:00
if rl.load_history("history.txt").is_err() {
println!("No previous history.");
}
2019-05-16 00:58:44 +02:00
let context = Arc::new(Mutex::new(Context::basic()?));
2019-05-10 18:59:12 +02:00
2019-05-15 18:12:38 +02:00
{
use crate::commands::*;
2019-05-10 18:59:12 +02:00
2019-05-15 18:12:38 +02:00
context.lock().unwrap().add_commands(vec![
2019-05-16 02:21:46 +02:00
("ps", Box::new(ps::Ps)),
("ls", Box::new(ls::Ls)),
("cd", Box::new(cd::Cd)),
2019-05-16 04:42:44 +02:00
("skip", Box::new(skip::Skip)),
2019-05-16 02:21:46 +02:00
("take", Box::new(take::Take)),
("select", Box::new(select::Select)),
("reject", Box::new(reject::Reject)),
("to-array", Box::new(to_array::ToArray)),
("where", Box::new(where_::Where)),
2019-05-15 18:12:38 +02:00
]);
}
2019-05-10 18:59:12 +02:00
loop {
2019-05-11 09:00:33 +02:00
let readline = rl.readline(&format!(
"{}> ",
context.lock().unwrap().env.cwd().display().to_string()
2019-05-11 09:00:33 +02:00
));
2019-05-11 06:45:57 +02:00
2019-05-16 23:43:36 +02:00
match process_line(readline, context.clone()) {
LineResult::Success(line) => {
rl.add_history_entry(line.as_ref());
}
LineResult::Error(err) => {
context.lock().unwrap().host.stdout(&err);
}
LineResult::Break => {
2019-05-16 04:42:44 +02:00
break;
}
2019-05-11 06:45:57 +02:00
2019-05-16 23:43:36 +02:00
LineResult::FatalError(err) => {
context
.lock()
.unwrap()
.host
.stdout(&format!("A surprising fatal error occurred.\n{:?}", err));
}
}
}
rl.save_history("history.txt").unwrap();
2019-05-11 06:45:57 +02:00
2019-05-16 23:43:36 +02:00
Ok(())
}
2019-05-10 18:59:12 +02:00
2019-05-16 23:43:36 +02:00
enum LineResult {
Success(String),
Error(String),
Break,
2019-05-16 23:43:36 +02:00
#[allow(unused)]
FatalError(ShellError),
}
fn process_line(
readline: Result<String, ReadlineError>,
context: Arc<Mutex<Context>>,
) -> LineResult {
match &readline {
Ok(line) if line.trim() == "exit" => LineResult::Break,
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
2019-05-10 18:59:12 +02:00
2019-05-16 23:43:36 +02:00
Ok(line) => {
let result = match crate::parser::shell_parser(&line) {
Err(err) => {
return LineResult::Error(format!("{:?}", err));
2019-05-10 18:59:12 +02:00
}
2019-05-16 23:43:36 +02:00
Ok(val) => val,
};
let parsed = result.1;
let mut input = VecDeque::new();
for item in parsed {
input = match process_command(
crate::parser::print_items(&item),
item.clone(),
input,
context.clone(),
) {
Ok(val) => val,
Err(err) => return LineResult::Error(format!("{}", err.description())),
};
2019-05-10 18:59:12 +02:00
}
2019-05-16 23:43:36 +02:00
if input.len() > 0 {
if equal_shapes(&input) {
format(crate::commands::to_array(input), context.clone());
} else {
format(input, context.clone());
}
2019-05-10 18:59:12 +02:00
}
2019-05-16 23:43:36 +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-10 18:59:12 +02:00
}
}
}
2019-05-13 23:00:25 +02:00
fn process_command(
line: String,
parsed: Vec<crate::parser::Item>,
input: VecDeque<Value>,
2019-05-15 18:12:38 +02:00
context: Arc<Mutex<Context>>,
2019-05-13 23:00:25 +02:00
) -> Result<VecDeque<Value>, ShellError> {
2019-05-16 23:43:36 +02:00
let command = &parsed[0].name()?;
2019-05-15 18:12:38 +02:00
let arg_list = parsed[1..].iter().map(|i| i.as_value()).collect();
2019-05-13 23:00:25 +02:00
2019-05-15 18:12:38 +02:00
if command == &"format" {
format(input, context);
2019-05-13 23:00:25 +02:00
2019-05-15 18:12:38 +02:00
Ok(VecDeque::new())
} else if command == &"format-list" {
let view = EntriesListView::from_stream(input);
2019-05-13 23:00:25 +02:00
2019-05-16 00:58:44 +02:00
crate::format::print_view(&view, context.clone());
2019-05-15 18:12:38 +02:00
Ok(VecDeque::new())
} else {
let mut ctx = context.lock().unwrap();
2019-05-13 23:00:25 +02:00
2019-05-15 18:12:38 +02:00
match ctx.has_command(*command) {
true => {
2019-05-16 02:21:46 +02:00
// let mut instance = ctx.create_command(command, arg_list)?;
let result = ctx.run_command(command, arg_list, input)?;
2019-05-13 23:00:25 +02:00
2019-05-16 02:21:46 +02:00
// let result = command.run(input_args)?;
2019-05-13 23:00:25 +02:00
let mut next = VecDeque::new();
for v in result {
match v {
ReturnValue::Action(action) => match action {
2019-05-15 18:12:38 +02:00
crate::CommandAction::ChangeCwd(cwd) => ctx.env.cwd = cwd,
2019-05-13 23:00:25 +02:00
},
ReturnValue::Value(v) => next.push_back(v),
}
}
Ok(next)
}
2019-05-15 18:12:38 +02:00
false => {
Exec::shell(line).cwd(ctx.env.cwd()).join().unwrap();
2019-05-13 23:00:25 +02:00
Ok(VecDeque::new())
}
2019-05-15 18:12:38 +02:00
}
2019-05-13 23:00:25 +02:00
}
}
2019-05-15 18:12:38 +02:00
fn format(input: VecDeque<Value>, context: Arc<Mutex<Context>>) {
let last = input.len() - 1;
for (i, item) in input.iter().enumerate() {
2019-05-16 00:23:36 +02:00
let view = GenericView::new(item);
2019-05-16 00:58:44 +02:00
crate::format::print_view(&view, context.clone());
2019-05-15 18:12:38 +02:00
if last != i {
println!("");
}
}
}
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
}