mirror of
https://github.com/nushell/nushell.git
synced 2024-11-24 17:34:00 +01:00
Basic pipelining is working!
This commit is contained in:
parent
975ff7c2fb
commit
3040638881
100
history.txt
100
history.txt
@ -1,100 +0,0 @@
|
|||||||
cargo install cargo-edit
|
|
||||||
ls
|
|
||||||
ls | foo | bar
|
|
||||||
ls
|
|
||||||
ls | ap
|
|
||||||
ls ab cd
|
|
||||||
ls ab cd | de
|
|
||||||
ls ab cd | grep 1243
|
|
||||||
ls ab cd|grep 1243
|
|
||||||
ls
|
|
||||||
hello world
|
|
||||||
hello world | zomg
|
|
||||||
ls
|
|
||||||
ls | zomgt
|
|
||||||
ls | zomg
|
|
||||||
ls soms
|
|
||||||
ls something
|
|
||||||
ls something | grep
|
|
||||||
dir
|
|
||||||
npm --help
|
|
||||||
cd target
|
|
||||||
dir
|
|
||||||
ls
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
dir
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
ls
|
|
||||||
dir
|
|
||||||
cd target
|
|
||||||
ls
|
|
||||||
cd ..
|
|
||||||
ls
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
ls
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cargo build
|
|
||||||
cargo build --verbose
|
|
||||||
ls
|
|
||||||
cargo
|
|
||||||
cargo build
|
|
||||||
cargo run
|
|
||||||
git status
|
|
||||||
git add .
|
|
||||||
git commit
|
|
||||||
git push
|
|
||||||
git status
|
|
||||||
git add .
|
|
||||||
git commit
|
|
||||||
git push
|
|
||||||
git config --global core.autocrlf input
|
|
||||||
git config
|
|
||||||
ls
|
|
||||||
cd target
|
|
||||||
ls
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
cd target
|
|
||||||
ls
|
|
||||||
git status
|
|
||||||
git add .
|
|
||||||
git commit
|
|
||||||
git push
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
exit
|
|
||||||
ls
|
|
||||||
ps
|
|
||||||
to-array
|
|
||||||
exit
|
|
||||||
ls
|
|
||||||
dir
|
|
||||||
ls
|
|
||||||
cd target
|
|
||||||
cd ..
|
|
||||||
ps
|
|
||||||
ls
|
|
||||||
dir
|
|
||||||
ls
|
|
||||||
ls | to-array
|
|
||||||
ls | format
|
|
||||||
ls | to-array | format
|
|
||||||
git status
|
|
@ -3,6 +3,8 @@ crate mod cd;
|
|||||||
crate mod command;
|
crate mod command;
|
||||||
crate mod ls;
|
crate mod ls;
|
||||||
crate mod ps;
|
crate mod ps;
|
||||||
|
crate mod take;
|
||||||
crate mod to_array;
|
crate mod to_array;
|
||||||
|
|
||||||
crate use command::Command;
|
crate use command::Command;
|
||||||
|
crate use to_array::to_array;
|
||||||
|
@ -12,10 +12,12 @@ pub trait CommandBlueprint {
|
|||||||
) -> Result<Box<dyn Command>, ShellError>;
|
) -> Result<Box<dyn Command>, ShellError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum CommandAction {
|
pub enum CommandAction {
|
||||||
ChangeCwd(PathBuf),
|
ChangeCwd(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ReturnValue {
|
pub enum ReturnValue {
|
||||||
Value(Value),
|
Value(Value),
|
||||||
Action(CommandAction),
|
Action(CommandAction),
|
||||||
|
@ -9,9 +9,7 @@ use std::rc::Rc;
|
|||||||
use sysinfo::SystemExt;
|
use sysinfo::SystemExt;
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
pub struct PsBlueprint {
|
pub struct PsBlueprint;
|
||||||
system: Rc<RefCell<sysinfo::System>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::CommandBlueprint for PsBlueprint {
|
impl crate::CommandBlueprint for PsBlueprint {
|
||||||
fn create(
|
fn create(
|
||||||
@ -20,18 +18,16 @@ impl crate::CommandBlueprint for PsBlueprint {
|
|||||||
host: &dyn crate::Host,
|
host: &dyn crate::Host,
|
||||||
env: &mut crate::Environment,
|
env: &mut crate::Environment,
|
||||||
) -> Result<Box<dyn Command>, ShellError> {
|
) -> Result<Box<dyn Command>, ShellError> {
|
||||||
Ok(Box::new(Ps::new(self.system.clone())))
|
Ok(Box::new(Ps::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
pub struct Ps {
|
pub struct Ps;
|
||||||
system: Rc<RefCell<sysinfo::System>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::Command for Ps {
|
impl crate::Command for Ps {
|
||||||
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> {
|
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> {
|
||||||
let mut system = self.system.borrow_mut();
|
let mut system = sysinfo::System::new();
|
||||||
system.refresh_all();
|
system.refresh_all();
|
||||||
|
|
||||||
let list = system.get_process_list();
|
let list = system.get_process_list();
|
||||||
@ -41,7 +37,6 @@ impl crate::Command for Ps {
|
|||||||
.map(|(_, process)| {
|
.map(|(_, process)| {
|
||||||
ReturnValue::Value(Value::Object(Box::new(Process::new(process.clone()))))
|
ReturnValue::Value(Value::Object(Box::new(Process::new(process.clone()))))
|
||||||
})
|
})
|
||||||
.take(5)
|
|
||||||
.collect::<VecDeque<_>>();
|
.collect::<VecDeque<_>>();
|
||||||
|
|
||||||
Ok(list)
|
Ok(list)
|
||||||
|
51
src/commands/take.rs
Normal file
51
src/commands/take.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use crate::errors::ShellError;
|
||||||
|
use crate::object::process::Process;
|
||||||
|
use crate::object::{DirEntry, ShellObject, Value};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::Args;
|
||||||
|
use derive_new::new;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use sysinfo::SystemExt;
|
||||||
|
|
||||||
|
#[derive(new)]
|
||||||
|
pub struct TakeBlueprint;
|
||||||
|
|
||||||
|
impl crate::CommandBlueprint for TakeBlueprint {
|
||||||
|
fn create(
|
||||||
|
&self,
|
||||||
|
args: Vec<Value>,
|
||||||
|
host: &dyn Host,
|
||||||
|
env: &mut Environment,
|
||||||
|
) -> Result<Box<dyn Command>, ShellError> {
|
||||||
|
if args.is_empty() {
|
||||||
|
return Err(ShellError::string("take requires an integer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let amount = args[0].as_int()?;
|
||||||
|
|
||||||
|
Ok(Box::new(Take { amount }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(new)]
|
||||||
|
pub struct Take {
|
||||||
|
amount: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::Command for Take {
|
||||||
|
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> {
|
||||||
|
let amount = if stream.len() > self.amount as usize {
|
||||||
|
self.amount as usize
|
||||||
|
} else {
|
||||||
|
stream.len()
|
||||||
|
};
|
||||||
|
|
||||||
|
let out: VecDeque<ReturnValue> = stream
|
||||||
|
.into_iter()
|
||||||
|
.take(amount)
|
||||||
|
.map(|v| ReturnValue::Value(v))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
@ -30,3 +30,10 @@ impl crate::Command for ToArray {
|
|||||||
Ok(ReturnValue::single(Value::List(out)))
|
Ok(ReturnValue::single(Value::List(out)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate fn to_array(stream: VecDeque<Value>) -> VecDeque<Value> {
|
||||||
|
let out = Value::List(stream.into_iter().collect());
|
||||||
|
let mut stream = VecDeque::new();
|
||||||
|
stream.push_back(out);
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
46
src/context.rs
Normal file
46
src/context.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub type Commands = BTreeMap<String, Box<dyn crate::CommandBlueprint>>;
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
commands: BTreeMap<String, Box<dyn crate::CommandBlueprint>>,
|
||||||
|
crate host: Box<dyn crate::Host>,
|
||||||
|
crate env: Environment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
crate fn basic() -> Result<Context, Box<Error>> {
|
||||||
|
Ok(Context {
|
||||||
|
commands: BTreeMap::new(),
|
||||||
|
host: Box::new(crate::env::host::BasicHost),
|
||||||
|
env: crate::Environment::basic()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_commands(&mut self, commands: Vec<(&str, Box<dyn crate::CommandBlueprint>)>) {
|
||||||
|
for (name, command) in commands {
|
||||||
|
self.commands.insert(name.to_string(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn get_command(&mut self, name: &str) -> Option<&dyn crate::CommandBlueprint> {
|
||||||
|
self.commands.get(name).map(|c| &**c)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn has_command(&mut self, name: &str) -> bool {
|
||||||
|
self.commands.contains_key(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn create_command(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
arg_list: Vec<Value>,
|
||||||
|
) -> Result<Box<dyn Command>, ShellError> {
|
||||||
|
match self.commands.get(name) {
|
||||||
|
Some(command) => Ok(command.create(arg_list, &self.host, &mut self.env)?),
|
||||||
|
None => Err(ShellError::string(format!("Missing command {}", name))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ crate mod table;
|
|||||||
use crate::object::Value;
|
use crate::object::Value;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
crate use entries::EntriesView;
|
crate use entries::{EntriesListView, EntriesView};
|
||||||
crate use generic::GenericView;
|
crate use generic::GenericView;
|
||||||
crate use list::ListView;
|
crate use list::ListView;
|
||||||
crate use table::TableView;
|
crate use table::TableView;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::format::RenderView;
|
use crate::format::RenderView;
|
||||||
|
use crate::object::base::ToEntriesView;
|
||||||
|
use crate::prelude::*;
|
||||||
use crate::Host;
|
use crate::Host;
|
||||||
|
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
|
||||||
// An entries list is printed like this:
|
// An entries list is printed like this:
|
||||||
@ -26,3 +29,38 @@ impl RenderView for EntriesView {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct EntriesListView {
|
||||||
|
values: VecDeque<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntriesListView {
|
||||||
|
crate fn from_stream(values: VecDeque<Value>) -> EntriesListView {
|
||||||
|
EntriesListView { values }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderView for EntriesListView {
|
||||||
|
fn render_view(&self, host: &dyn Host) -> Vec<String> {
|
||||||
|
if self.values.len() == 0 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut strings = vec![];
|
||||||
|
|
||||||
|
let last = self.values.len() - 1;
|
||||||
|
|
||||||
|
for (i, item) in self.values.iter().enumerate() {
|
||||||
|
let view = item.to_entries_view();
|
||||||
|
let out = view.render_view(host);
|
||||||
|
|
||||||
|
strings.extend(out);
|
||||||
|
|
||||||
|
if i != last {
|
||||||
|
strings.push("\n".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
139
src/main.rs
139
src/main.rs
@ -3,6 +3,7 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
|
mod context;
|
||||||
mod env;
|
mod env;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod format;
|
mod format;
|
||||||
@ -13,11 +14,12 @@ mod prelude;
|
|||||||
crate use crate::commands::args::{Args, Streams};
|
crate use crate::commands::args::{Args, Streams};
|
||||||
use crate::commands::command::ReturnValue;
|
use crate::commands::command::ReturnValue;
|
||||||
crate use crate::commands::command::{Command, CommandAction, CommandBlueprint};
|
crate use crate::commands::command::{Command, CommandAction, CommandBlueprint};
|
||||||
|
use crate::context::Context;
|
||||||
crate use crate::env::{Environment, Host};
|
crate use crate::env::{Environment, Host};
|
||||||
crate use crate::errors::ShellError;
|
crate use crate::errors::ShellError;
|
||||||
crate use crate::format::RenderView;
|
crate use crate::format::{EntriesListView, RenderView};
|
||||||
use crate::object::base::{ToEntriesView, ToGenericView};
|
use crate::object::base::{ToEntriesView, ToGenericView};
|
||||||
use crate::object::Value;
|
use crate::object::{ShellObject, Value};
|
||||||
|
|
||||||
use ansi_term::Color;
|
use ansi_term::Color;
|
||||||
use conch_parser::lexer::Lexer;
|
use conch_parser::lexer::Lexer;
|
||||||
@ -29,6 +31,7 @@ use std::collections::BTreeMap;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use subprocess::Exec;
|
use subprocess::Exec;
|
||||||
use sysinfo::{self, SystemExt};
|
use sysinfo::{self, SystemExt};
|
||||||
|
|
||||||
@ -47,51 +50,30 @@ impl<T> MaybeOwned<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Commands = BTreeMap<String, Box<dyn crate::CommandBlueprint>>;
|
|
||||||
|
|
||||||
struct Context {
|
|
||||||
commands: BTreeMap<String, Box<dyn crate::CommandBlueprint>>,
|
|
||||||
host: Box<dyn crate::Host>,
|
|
||||||
env: Environment,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
fn basic() -> Result<Context, Box<Error>> {
|
|
||||||
Ok(Context {
|
|
||||||
commands: BTreeMap::new(),
|
|
||||||
host: Box::new(crate::env::host::BasicHost),
|
|
||||||
env: crate::Environment::basic()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<Error>> {
|
fn main() -> Result<(), Box<Error>> {
|
||||||
let mut rl = Editor::<()>::new();
|
let mut rl = Editor::<()>::new();
|
||||||
if rl.load_history("history.txt").is_err() {
|
if rl.load_history("history.txt").is_err() {
|
||||||
println!("No previous history.");
|
println!("No previous history.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut context = Context::basic()?;
|
let mut context = Arc::new(Mutex::new(Context::basic()?));
|
||||||
|
|
||||||
// let mut commands = BTreeMap::<String, Box<dyn crate::CommandBlueprint>>::new();
|
{
|
||||||
|
use crate::commands::*;
|
||||||
|
|
||||||
let mut system = Rc::new(RefCell::new(sysinfo::System::new()));
|
context.lock().unwrap().add_commands(vec![
|
||||||
let mut ps = crate::commands::ps::PsBlueprint::new(system);
|
("ps", Box::new(ps::PsBlueprint)),
|
||||||
let mut ls = crate::commands::ls::LsBlueprint;
|
("ls", Box::new(ls::LsBlueprint)),
|
||||||
let mut cd = crate::commands::cd::CdBlueprint;
|
("cd", Box::new(cd::CdBlueprint)),
|
||||||
let mut to_array = crate::commands::to_array::ToArrayBlueprint;
|
("take", Box::new(take::TakeBlueprint)),
|
||||||
|
("to-array", Box::new(to_array::ToArrayBlueprint)),
|
||||||
context.commands.insert("ps".to_string(), Box::new(ps));
|
]);
|
||||||
context.commands.insert("ls".to_string(), Box::new(ls));
|
}
|
||||||
context.commands.insert("cd".to_string(), Box::new(cd));
|
|
||||||
context
|
|
||||||
.commands
|
|
||||||
.insert("to-array".to_string(), Box::new(to_array));
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let readline = rl.readline(&format!(
|
let readline = rl.readline(&format!(
|
||||||
"{}> ",
|
"{}> ",
|
||||||
Color::Green.paint(context.env.cwd().display().to_string())
|
Color::Green.paint(context.lock().unwrap().env.cwd().display().to_string())
|
||||||
));
|
));
|
||||||
|
|
||||||
match readline {
|
match readline {
|
||||||
@ -105,16 +87,23 @@ fn main() -> Result<(), Box<Error>> {
|
|||||||
|
|
||||||
let mut input = VecDeque::new();
|
let mut input = VecDeque::new();
|
||||||
|
|
||||||
|
let last = parsed.len() - 1;
|
||||||
|
|
||||||
for item in parsed {
|
for item in parsed {
|
||||||
// println!("Processing {:?}", item);
|
|
||||||
input = process_command(
|
input = process_command(
|
||||||
crate::parser::print_items(&item),
|
crate::parser::print_items(&item),
|
||||||
item.clone(),
|
item.clone(),
|
||||||
input,
|
input,
|
||||||
&mut context,
|
context.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
// println!("OUTPUT: {:?}", input);
|
if input.len() > 0 {
|
||||||
|
if equal_shapes(&input) {
|
||||||
|
format(crate::commands::to_array(input), context.clone());
|
||||||
|
} else {
|
||||||
|
format(input, context.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
@ -140,31 +129,30 @@ fn process_command(
|
|||||||
line: String,
|
line: String,
|
||||||
parsed: Vec<crate::parser::Item>,
|
parsed: Vec<crate::parser::Item>,
|
||||||
input: VecDeque<Value>,
|
input: VecDeque<Value>,
|
||||||
context: &mut Context,
|
context: Arc<Mutex<Context>>,
|
||||||
) -> Result<VecDeque<Value>, ShellError> {
|
) -> Result<VecDeque<Value>, ShellError> {
|
||||||
let command = &parsed[0].name();
|
let command = &parsed[0].name();
|
||||||
let arg_list = parsed[1..]
|
let arg_list = parsed[1..].iter().map(|i| i.as_value()).collect();
|
||||||
.iter()
|
|
||||||
.map(|i| Value::string(i.name().to_string()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let streams = Streams::new();
|
let streams = Streams::new();
|
||||||
|
|
||||||
// let args = Args::new(arg_list);
|
if command == &"format" {
|
||||||
|
format(input, context);
|
||||||
|
|
||||||
match *command {
|
Ok(VecDeque::new())
|
||||||
"format" => {
|
} else if command == &"format-list" {
|
||||||
for item in input {
|
let view = EntriesListView::from_stream(input);
|
||||||
let view = item.to_generic_view();
|
let mut ctx = context.lock().unwrap();
|
||||||
crate::format::print_rendered(&view.render_view(&context.host), &mut context.host);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(VecDeque::new())
|
crate::format::print_rendered(&view.render_view(&ctx.host), &mut ctx.host);
|
||||||
}
|
|
||||||
|
|
||||||
command => match context.commands.get_mut(command) {
|
Ok(VecDeque::new())
|
||||||
Some(command) => {
|
} else {
|
||||||
let mut instance = command.create(arg_list, &context.host, &mut context.env)?;
|
let mut ctx = context.lock().unwrap();
|
||||||
|
|
||||||
|
match ctx.has_command(*command) {
|
||||||
|
true => {
|
||||||
|
let mut instance = ctx.create_command(command, arg_list)?;
|
||||||
|
|
||||||
let mut result = instance.run(input)?;
|
let mut result = instance.run(input)?;
|
||||||
let mut next = VecDeque::new();
|
let mut next = VecDeque::new();
|
||||||
@ -172,7 +160,7 @@ fn process_command(
|
|||||||
for v in result {
|
for v in result {
|
||||||
match v {
|
match v {
|
||||||
ReturnValue::Action(action) => match action {
|
ReturnValue::Action(action) => match action {
|
||||||
crate::CommandAction::ChangeCwd(cwd) => context.env.cwd = cwd,
|
crate::CommandAction::ChangeCwd(cwd) => ctx.env.cwd = cwd,
|
||||||
},
|
},
|
||||||
|
|
||||||
ReturnValue::Value(v) => next.push_back(v),
|
ReturnValue::Value(v) => next.push_back(v),
|
||||||
@ -182,10 +170,43 @@ fn process_command(
|
|||||||
Ok(next)
|
Ok(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
other => {
|
false => {
|
||||||
Exec::shell(line).cwd(context.env.cwd()).join().unwrap();
|
Exec::shell(line).cwd(ctx.env.cwd()).join().unwrap();
|
||||||
Ok(VecDeque::new())
|
Ok(VecDeque::new())
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format(input: VecDeque<Value>, context: Arc<Mutex<Context>>) {
|
||||||
|
let mut ctx = context.lock().unwrap();
|
||||||
|
|
||||||
|
let last = input.len() - 1;
|
||||||
|
for (i, item) in input.iter().enumerate() {
|
||||||
|
let view = item.to_generic_view();
|
||||||
|
crate::format::print_rendered(&view.render_view(&ctx.host), &mut ctx.host);
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
@ -42,6 +42,7 @@ impl ShellObject for Value {
|
|||||||
Value::List(l) => format!("[list List]"),
|
Value::List(l) => format!("[list List]"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_descriptors(&self) -> Vec<DataDescriptor> {
|
fn data_descriptors(&self) -> Vec<DataDescriptor> {
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(p) => vec![],
|
Value::Primitive(p) => vec![],
|
||||||
@ -80,6 +81,17 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate fn as_int(&self) -> Result<i64, ShellError> {
|
||||||
|
match self {
|
||||||
|
Value::Primitive(Primitive::Int(i)) => Ok(*i),
|
||||||
|
// TODO: this should definitely be more general with better errors
|
||||||
|
other => Err(ShellError::string(format!(
|
||||||
|
"Expected integer, got {:?}",
|
||||||
|
other
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crate fn string(s: impl Into<String>) -> Value {
|
crate fn string(s: impl Into<String>) -> Value {
|
||||||
Value::Primitive(Primitive::String(s.into()))
|
Value::Primitive(Primitive::String(s.into()))
|
||||||
}
|
}
|
||||||
@ -115,7 +127,10 @@ pub trait ToEntriesView {
|
|||||||
fn to_entries_view(&self) -> EntriesView;
|
fn to_entries_view(&self) -> EntriesView;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToEntriesView for ShellObject {
|
impl<T> ToEntriesView for T
|
||||||
|
where
|
||||||
|
T: ShellObject,
|
||||||
|
{
|
||||||
fn to_entries_view(&self) -> EntriesView {
|
fn to_entries_view(&self) -> EntriesView {
|
||||||
let descs = self.data_descriptors();
|
let descs = self.data_descriptors();
|
||||||
let mut entries = vec![];
|
let mut entries = vec![];
|
||||||
@ -136,6 +151,19 @@ impl ToEntriesView for ShellObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ShellObject for Box<dyn ShellObject> {
|
||||||
|
fn to_shell_string(&self) -> String {
|
||||||
|
(**self).to_shell_string()
|
||||||
|
}
|
||||||
|
fn data_descriptors(&self) -> Vec<DataDescriptor> {
|
||||||
|
(**self).data_descriptors()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_data(&'a self, desc: &DataDescriptor) -> crate::MaybeOwned<'a, Value> {
|
||||||
|
(**self).get_data(desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ToGenericView {
|
pub trait ToGenericView {
|
||||||
fn to_generic_view(&self) -> GenericView;
|
fn to_generic_view(&self) -> GenericView;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::object::types::{Any, Type};
|
use crate::object::types::{AnyShell, Type};
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
@ -8,12 +8,18 @@ pub struct DataDescriptor {
|
|||||||
crate ty: Box<dyn Type>,
|
crate ty: Box<dyn Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for DataDescriptor {
|
||||||
|
fn eq(&self, other: &DataDescriptor) -> bool {
|
||||||
|
self.name == other.name && self.readonly == other.readonly && self.ty.equal(&*other.ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DataDescriptor {
|
impl DataDescriptor {
|
||||||
crate fn any(name: impl Into<String>) -> DataDescriptor {
|
crate fn any(name: impl Into<String>) -> DataDescriptor {
|
||||||
DataDescriptor {
|
DataDescriptor {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
readonly: true,
|
readonly: true,
|
||||||
ty: Box::new(Any),
|
ty: Box::new(AnyShell),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ impl crate::object::ShellObject for Dictionary {
|
|||||||
self.entries
|
self.entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, value)| {
|
.map(|(name, value)| {
|
||||||
DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::Any))
|
DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::AnyShell))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ impl crate::object::ShellObject for ScopedDictionary<'parent> {
|
|||||||
self.entries
|
self.entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, value)| {
|
.map(|(name, value)| {
|
||||||
DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::Any))
|
DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::AnyShell))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
pub trait Type {}
|
use std::any::{Any, TypeId};
|
||||||
|
|
||||||
pub struct Any;
|
pub trait Type {
|
||||||
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
fn equal(&self, other: &dyn Type) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
impl Type for Any {}
|
#[derive(Eq, PartialEq)]
|
||||||
|
pub struct AnyShell;
|
||||||
|
|
||||||
|
impl Type for AnyShell {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self as &dyn Any
|
||||||
|
}
|
||||||
|
|
||||||
|
fn equal(&self, other: &dyn Type) -> bool {
|
||||||
|
other.as_any().is::<AnyShell>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,63 +1,4 @@
|
|||||||
use nom::branch::alt;
|
crate mod completer;
|
||||||
use nom::bytes::complete::{escaped, is_not, tag};
|
crate mod parse;
|
||||||
use nom::character::complete::one_of;
|
|
||||||
use nom::multi::separated_list;
|
|
||||||
use nom::sequence::{preceded, terminated};
|
|
||||||
use nom::IResult;
|
|
||||||
use nom::{complete, named, separated_list, ws};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
crate use self::parse::{print_items, shell_parser, Item};
|
||||||
pub enum Item {
|
|
||||||
Quoted(String),
|
|
||||||
Bare(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
crate fn print_items(items: &[Item]) -> String {
|
|
||||||
let mut out = String::new();
|
|
||||||
|
|
||||||
let formatted = items.iter().map(|item| match item {
|
|
||||||
Item::Bare(s) => format!("{}", s),
|
|
||||||
Item::Quoted(s) => format!("{:?}", s),
|
|
||||||
});
|
|
||||||
|
|
||||||
itertools::join(formatted, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Item {
|
|
||||||
crate fn name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Item::Quoted(s) => s,
|
|
||||||
Item::Bare(s) => s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn esc(s: &str) -> IResult<&str, &str> {
|
|
||||||
escaped(is_not("\\\""), '\\', one_of("\"n\\"))(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn quoted(s: &str) -> IResult<&str, Item> {
|
|
||||||
terminated(preceded(tag("\""), esc), tag("\""))(s)
|
|
||||||
.map(|(a, b)| (a, Item::Quoted(b.to_string())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unquoted(s: &str) -> IResult<&str, Item> {
|
|
||||||
is_not(" |")(s).map(|(a, b)| (a, Item::Bare(b.to_string())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_token(s: &str) -> IResult<&str, Item> {
|
|
||||||
alt((quoted, unquoted))(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_args(s: &str) -> IResult<&str, Vec<Item>> {
|
|
||||||
separated_list(tag(" "), command_token)(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(
|
|
||||||
pub shell_parser(&str) -> Vec<Vec<Item>>,
|
|
||||||
complete!(
|
|
||||||
ws!(
|
|
||||||
separated_list!(tag("|"), command_args)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
32
src/parser/completer.rs
Normal file
32
src/parser/completer.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use rustyline::{completion, Context};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
crate struct Completer {
|
||||||
|
commands: BTreeMap<String, Box<dyn crate::CommandBlueprint>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl completion::Completer for Completer {
|
||||||
|
type Candidate = completion::Pair;
|
||||||
|
|
||||||
|
fn complete(
|
||||||
|
&self,
|
||||||
|
line: &str,
|
||||||
|
pos: usize,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
) -> rustyline::Result<(usize, Vec<completion::Pair>)> {
|
||||||
|
let pairs = self
|
||||||
|
.commands
|
||||||
|
.keys()
|
||||||
|
.map(|k| completion::Pair {
|
||||||
|
display: k.clone(),
|
||||||
|
replacement: k.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok((0, pairs))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {
|
||||||
|
let end = line.pos();
|
||||||
|
line.replace(start..end, elected)
|
||||||
|
}
|
||||||
|
}
|
82
src/parser/parse.rs
Normal file
82
src/parser/parse.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::{escaped, is_a, is_not, tag};
|
||||||
|
use nom::character::complete::one_of;
|
||||||
|
use nom::multi::separated_list;
|
||||||
|
use nom::sequence::{preceded, terminated};
|
||||||
|
use nom::IResult;
|
||||||
|
use nom::{complete, named, separated_list, ws};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Item {
|
||||||
|
Quoted(String),
|
||||||
|
Bare(String),
|
||||||
|
Int(i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
crate fn as_value(&self) -> Value {
|
||||||
|
match self {
|
||||||
|
Item::Quoted(s) => Value::Primitive(Primitive::String(s.clone())),
|
||||||
|
Item::Bare(s) => Value::Primitive(Primitive::String(s.clone())),
|
||||||
|
Item::Int(i) => Value::Primitive(Primitive::Int(*i)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn print_items(items: &[Item]) -> String {
|
||||||
|
let mut out = String::new();
|
||||||
|
|
||||||
|
let formatted = items.iter().map(|item| match item {
|
||||||
|
Item::Bare(s) => format!("{}", s),
|
||||||
|
Item::Quoted(s) => format!("{:?}", s),
|
||||||
|
Item::Int(i) => format!("{:?}", i),
|
||||||
|
});
|
||||||
|
|
||||||
|
itertools::join(formatted, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
crate fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Item::Quoted(s) => s,
|
||||||
|
Item::Bare(s) => s,
|
||||||
|
Item::Int(i) => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn esc(s: &str) -> IResult<&str, &str> {
|
||||||
|
escaped(is_not("\\\""), '\\', one_of("\"n\\"))(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quoted(s: &str) -> IResult<&str, Item> {
|
||||||
|
terminated(preceded(tag("\""), esc), tag("\""))(s)
|
||||||
|
.map(|(a, b)| (a, Item::Quoted(b.to_string())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unquoted(s: &str) -> IResult<&str, Item> {
|
||||||
|
is_not(" |")(s).map(|(a, b)| (a, Item::Bare(b.to_string())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int(s: &str) -> IResult<&str, Item> {
|
||||||
|
is_a("1234567890")(s).map(|(a, b)| (a, Item::Int(FromStr::from_str(b).unwrap())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_token(s: &str) -> IResult<&str, Item> {
|
||||||
|
alt((int, quoted, unquoted))(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_args(s: &str) -> IResult<&str, Vec<Item>> {
|
||||||
|
separated_list(tag(" "), command_token)(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(
|
||||||
|
pub shell_parser(&str) -> Vec<Vec<Item>>,
|
||||||
|
complete!(
|
||||||
|
ws!(
|
||||||
|
separated_list!(tag("|"), command_args)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
@ -3,4 +3,5 @@ crate use crate::commands::command::{Command, CommandAction, CommandBlueprint, R
|
|||||||
crate use crate::env::{Environment, Host};
|
crate use crate::env::{Environment, Host};
|
||||||
crate use crate::errors::ShellError;
|
crate use crate::errors::ShellError;
|
||||||
crate use crate::format::RenderView;
|
crate use crate::format::RenderView;
|
||||||
|
crate use crate::object::{Primitive, Value};
|
||||||
crate use std::collections::VecDeque;
|
crate use std::collections::VecDeque;
|
||||||
|
Loading…
Reference in New Issue
Block a user