init
This commit is contained in:
commit
50173b1ed5
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "custom_cli"
|
||||
version = "0.1.0"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "custom_cli"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
15
README.md
Normal file
15
README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# rShell
|
||||
|
||||
Writing a shell inspired by bash (obviously) but also inspired by Ruckus and Cisco's CLIs where you don't have to type out the full comamnd. As long as it is distinct. Simmilar to how the `ip` command words in Linux. Where
|
||||
|
||||
```bash
|
||||
ip address
|
||||
```
|
||||
|
||||
and this
|
||||
|
||||
```bash
|
||||
ip a
|
||||
```
|
||||
|
||||
are the same thing.
|
104
src/main.rs
Normal file
104
src/main.rs
Normal file
@ -0,0 +1,104 @@
|
||||
#![feature(iter_intersperse)]
|
||||
use std::io::{BufRead, Write};
|
||||
|
||||
fn main() {
|
||||
|
||||
let stdin = std::io::stdin();
|
||||
let mut in_lock = stdin.lock();
|
||||
|
||||
let stdout = std::io::stdout();
|
||||
let mut out_lock = stdout.lock();
|
||||
|
||||
loop {
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
let ps1 = "> ".as_bytes();
|
||||
out_lock.write(ps1).unwrap();
|
||||
out_lock.flush().unwrap();
|
||||
|
||||
in_lock.read_line(&mut buf).expect("Failed to read in to buffer.");
|
||||
let trim = buf.trim();
|
||||
|
||||
if let Some(choice) = Command::choose(trim, &commands()) {
|
||||
let words = trim.split(' ').collect::<Vec<&str>>();
|
||||
choice.call(words);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn commands() -> Vec<Command> {
|
||||
|
||||
let commands = vec![
|
||||
Command::init("ls", vec![], nil),
|
||||
Command::init("exit", vec![], |_| {
|
||||
std::process::exit(0);
|
||||
}),
|
||||
Command::init("echo", vec![], |f| {
|
||||
let msg = &f[1..].iter().map(|s| s.to_owned()).intersperse(" ").collect::<String>();
|
||||
println!("{msg}");
|
||||
}),
|
||||
];
|
||||
|
||||
commands
|
||||
}
|
||||
|
||||
type Param<'a> = Vec<&'a str>;
|
||||
type Run = fn(Param) -> ();
|
||||
fn nil(_: Param) {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Command {
|
||||
name: String,
|
||||
sub_commands: Vec<Command>,
|
||||
cmd: Run,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn init(name: &str, sub_commands: Vec<Command>, f: Run) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
sub_commands,
|
||||
cmd: f,
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, words: Param) {
|
||||
let x = self.cmd;
|
||||
x(words)
|
||||
}
|
||||
|
||||
fn choose<'a>(text: &str, choices: &'a Vec<Command>) -> Option<&'a Command> {
|
||||
|
||||
// See what command the user is trying to call
|
||||
// iter thru all the commands
|
||||
let mut ranked = choices.iter().fold(Vec::new(), |mut v, cmd| {
|
||||
// count how many chars match from the command to the word
|
||||
let matches = text.chars().zip(cmd.name.chars()).fold(0, |mut f, (word, cmd)| {
|
||||
if word == cmd { f += 1; }
|
||||
f
|
||||
});
|
||||
v.push((cmd, matches));
|
||||
v
|
||||
});
|
||||
|
||||
// sort by most matches
|
||||
ranked.sort_by(|a,b| b.1.cmp(&a.1));
|
||||
|
||||
if ranked.len() > 1 {
|
||||
// If the top two command choices are ranked the same, we don't know which to choose!
|
||||
if ranked[0].1 == ranked[1].1 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((cmd, _)) = ranked.first() {
|
||||
// let words = text.split(' ').collect::<Vec<&str>>();
|
||||
|
||||
return Some(cmd);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user