mirror of
https://github.com/danbulant/rush
synced 2026-07-06 03:30:44 +00:00
better CLI
This commit is contained in:
parent
b0b6eb7d48
commit
33571b0c03
5 changed files with 143 additions and 13 deletions
91
Cargo.lock
generated
91
Cargo.lock
generated
|
|
@ -32,6 +32,17 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -71,6 +82,21 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5f1fea81f183005ced9e59cdb01737ef2423956dac5a6d731b06b2ecfaa3467"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"indexmap",
|
||||||
|
"os_str_bytes",
|
||||||
|
"strsim",
|
||||||
|
"termcolor",
|
||||||
|
"textwrap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filedescriptor"
|
name = "filedescriptor"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
|
@ -88,6 +114,31 @@ version = "0.26.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.107"
|
version = "0.2.107"
|
||||||
|
|
@ -125,6 +176,15 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_str_bytes"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.32"
|
version = "1.0.32"
|
||||||
|
|
@ -166,6 +226,7 @@ name = "rush"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"clap",
|
||||||
"filedescriptor",
|
"filedescriptor",
|
||||||
"termion",
|
"termion",
|
||||||
"utf8-chars",
|
"utf8-chars",
|
||||||
|
|
@ -177,6 +238,12 @@ version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.82"
|
version = "1.0.82"
|
||||||
|
|
@ -188,6 +255,15 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termion"
|
name = "termion"
|
||||||
version = "1.5.6"
|
version = "1.5.6"
|
||||||
|
|
@ -200,6 +276,12 @@ dependencies = [
|
||||||
"redox_termios",
|
"redox_termios",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.30"
|
version = "1.0.30"
|
||||||
|
|
@ -251,6 +333,15 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ name = "rush"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Daniel Bulant <danbulant@gmail.com>"]
|
authors = ["Daniel Bulant <danbulant@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
description = "A simple rust shell"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|
@ -10,6 +11,7 @@ edition = "2018"
|
||||||
utf8-chars = "1.0.0"
|
utf8-chars = "1.0.0"
|
||||||
termion = "1.5.6"
|
termion = "1.5.6"
|
||||||
filedescriptor = "0.8.1"
|
filedescriptor = "0.8.1"
|
||||||
|
clap = "3.1.0"
|
||||||
|
|
||||||
[dependencies.anyhow]
|
[dependencies.anyhow]
|
||||||
version = "1.0.54"
|
version = "1.0.54"
|
||||||
|
|
|
||||||
51
src/main.rs
51
src/main.rs
|
|
@ -2,14 +2,17 @@ mod parser;
|
||||||
|
|
||||||
use std::io::{self, BufRead, Stdout, Write};
|
use std::io::{self, BufRead, Stdout, Write};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use std::path::Path;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
use clap::{Arg, Command, arg};
|
||||||
use termion::raw::{IntoRawMode, RawTerminal};
|
use termion::raw::{IntoRawMode, RawTerminal};
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
use termion::cursor::{DetectCursorPos};
|
use termion::cursor::{DetectCursorPos};
|
||||||
use termion::event::*;
|
use termion::event::*;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
struct Term {
|
struct Term {
|
||||||
input: String,
|
input: String,
|
||||||
|
|
@ -70,7 +73,7 @@ impl Shell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn start_shell() {
|
||||||
let mut shell = Shell::new();
|
let mut shell = Shell::new();
|
||||||
loop {
|
loop {
|
||||||
print!("$: ");
|
print!("$: ");
|
||||||
|
|
@ -85,6 +88,48 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||||
|
const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
||||||
|
|
||||||
|
fn load_and_run<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||||
|
let mut ctx = parser::vars::Context::new();
|
||||||
|
let src = File::open(path).unwrap();
|
||||||
|
parser::exec(&mut BufReader::new(src), &mut ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let matches = Command::new("Rush")
|
||||||
|
.version(VERSION)
|
||||||
|
.author(AUTHORS)
|
||||||
|
.about(DESCRIPTION)
|
||||||
|
.arg(
|
||||||
|
arg!([file] "File to execute")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
arg!(-c --command <COMMAND> "Command to execute")
|
||||||
|
.required(false)
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
match matches.value_of("command") {
|
||||||
|
Some(command) => {
|
||||||
|
let mut ctx = parser::vars::Context::new();
|
||||||
|
parser::exec(&mut command.as_bytes(), &mut ctx).unwrap();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
};
|
||||||
|
match matches.value_of("file") {
|
||||||
|
Some(file) => {
|
||||||
|
load_and_run(file).unwrap();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
};
|
||||||
|
start_shell();
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
|
||||||
|
|
@ -400,10 +400,8 @@ impl Tree {
|
||||||
if lvl != 0 {
|
if lvl != 0 {
|
||||||
bail!("Parenthesis do not match");
|
bail!("Parenthesis do not match");
|
||||||
}
|
}
|
||||||
dbg!(&self, len);
|
|
||||||
let val = Value::Expressions(self.parse_sub(self.i + len)?);
|
let val = Value::Expressions(self.parse_sub(self.i + len)?);
|
||||||
self.inc();
|
self.inc();
|
||||||
dbg!(self);
|
|
||||||
return Ok(val);
|
return Ok(val);
|
||||||
},
|
},
|
||||||
Tokens::Else => buf.push(Value::Literal(token.to_str())),
|
Tokens::Else => buf.push(Value::Literal(token.to_str())),
|
||||||
|
|
@ -444,12 +442,10 @@ impl Tree {
|
||||||
let mut expr: Option<Expression> = None;
|
let mut expr: Option<Expression> = None;
|
||||||
let mut token = self.get_current_token();
|
let mut token = self.get_current_token();
|
||||||
loop {
|
loop {
|
||||||
dbg!(token);
|
|
||||||
match token {
|
match token {
|
||||||
Tokens::Space => {self.inc();},
|
Tokens::Space => {self.inc();},
|
||||||
Tokens::CommandEnd(_) => { if matches!(expr, Some(_)) { break }; self.inc();},
|
Tokens::CommandEnd(_) => { if matches!(expr, Some(_)) { break }; self.inc();},
|
||||||
Tokens::Literal(_) => if matches!(expr, Some(_)) {
|
Tokens::Literal(_) => if matches!(expr, Some(_)) {
|
||||||
dbg!(expr);
|
|
||||||
bail!("Unexpected literal. After file redirect, you need to use a semicolon or newline.");
|
bail!("Unexpected literal. After file redirect, you need to use a semicolon or newline.");
|
||||||
} else {
|
} else {
|
||||||
expr = Some(self.parse_call(end)?);
|
expr = Some(self.parse_call(end)?);
|
||||||
|
|
@ -498,14 +494,14 @@ impl Tree {
|
||||||
_ => expr = Some(self.parse_call(end)?)
|
_ => expr = Some(self.parse_call(end)?)
|
||||||
},
|
},
|
||||||
Tokens::Else => bail!("Unexpected token ELSE"),
|
Tokens::Else => bail!("Unexpected token ELSE"),
|
||||||
Tokens::End => { dbg!(expr); bail!("Unexpected token END"); },
|
Tokens::End => { bail!("Unexpected token END"); },
|
||||||
Tokens::For => match expr {
|
Tokens::For => match expr {
|
||||||
Some(_) => bail!("Commands must be ended properly"),
|
Some(_) => bail!("Commands must be ended properly"),
|
||||||
None => expr = Some(Expression::ForExpression(self.parse_for(end)?)),
|
None => expr = Some(Expression::ForExpression(self.parse_for(end)?)),
|
||||||
},
|
},
|
||||||
Tokens::If => match expr {
|
Tokens::If => match expr {
|
||||||
Some(_) => bail!("Commands must be ended properly"),
|
Some(_) => bail!("Commands must be ended properly"),
|
||||||
None => {expr = Some(Expression::IfExpression(self.parse_if(end)?)); dbg!(&expr); },
|
None => {expr = Some(Expression::IfExpression(self.parse_if(end)?)); },
|
||||||
}
|
}
|
||||||
Tokens::Let => return Ok(self.parse_let(end)?),
|
Tokens::Let => return Ok(self.parse_let(end)?),
|
||||||
Tokens::While => return Ok(Expression::WhileExpression(self.parse_while(end)?)),
|
Tokens::While => return Ok(Expression::WhileExpression(self.parse_while(end)?)),
|
||||||
|
|
@ -539,7 +535,6 @@ impl Tree {
|
||||||
Tokens::JobCommandEnd => bail!("Jobs not yet implemented")
|
Tokens::JobCommandEnd => bail!("Jobs not yet implemented")
|
||||||
}
|
}
|
||||||
if self.i >= end - 1 { break }
|
if self.i >= end - 1 { break }
|
||||||
dbg!("finished loop", &expr);
|
|
||||||
token = self.get_current_token();
|
token = self.get_current_token();
|
||||||
}
|
}
|
||||||
match expr {
|
match expr {
|
||||||
|
|
@ -566,7 +561,6 @@ pub fn build_tree(tokens: Vec<Tokens>) -> Result<Vec<Expression>> {
|
||||||
Ok(val) => expressions.push(val),
|
Ok(val) => expressions.push(val),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
if error.to_string() == "No expression found" { break }
|
if error.to_string() == "No expression found" { break }
|
||||||
dbg!(tree);
|
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ pub fn exec(reader: &mut dyn std::io::BufRead, ctx: &mut vars::Context) -> Resul
|
||||||
|
|
||||||
let expressions = build_tree(tokens)?;
|
let expressions = build_tree(tokens)?;
|
||||||
|
|
||||||
dbg!(&expressions);
|
|
||||||
|
|
||||||
exec_tree(expressions, ctx)?;
|
exec_tree(expressions, ctx)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue