This commit is contained in:
Oliver 2025-08-16 11:58:11 -06:00
parent 9cc1a64a91
commit 6de062e808
5 changed files with 81 additions and 56 deletions

17
src/cmd/echo.rs Normal file
View File

@ -0,0 +1,17 @@
use crate::command::Command;
pub fn init() -> Command {
Command::init(
"echo",
vec![],
Box::new(|args, _| {
let msg = &args[1..]
.iter()
.map(|s| s.to_owned())
.intersperse(" ".to_string())
.collect::<String>();
println!("{msg}");
0
}),
)
}

2
src/cmd/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod echo;
pub mod random;

31
src/cmd/random.rs Normal file
View File

@ -0,0 +1,31 @@
use crate::command::Command;
pub fn init() -> Command {
Command::init(
"random",
vec![
Command::init(
"number",
vec![],
Box::new(|_, _| {
// how do you know it's not random?
println!("8");
0
}),
),
Command::init(
"string",
vec![],
Box::new(|_, _| {
// how do you know it's not random?
println!("hello world");
0
}),
),
],
Box::new(|args, env| {
println!("args: {:?} env: {:?}", args, env);
0
}),
)
}

View File

@ -8,11 +8,23 @@ use std::{
use crate::SPECIAL_CHAR; use crate::SPECIAL_CHAR;
type InnerEnv = HashMap<String, String>; type InnerEnv = HashMap<String, String>;
pub struct Env { pub struct CmdEnv {
inner: InnerEnv, inner: InnerEnv,
} }
impl Env { impl std::fmt::Debug for CmdEnv {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut stu = f.debug_struct("CmdEnv");
for (k,v) in self.inner.iter() {
stu.field(k, v);
}
stu
.finish()
}
}
impl CmdEnv {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: HashMap::new(), inner: HashMap::new(),
@ -46,7 +58,8 @@ impl Env {
} }
pub type Args<'a, 'b> = &'b [String]; pub type Args<'a, 'b> = &'b [String];
trait Run = Fn(Args, &mut Env) -> i32; // A function that takes args, the env, and returns a status code
trait Run = Fn(Args, &mut CmdEnv) -> i32;
type Cmd = Box<dyn Run>; type Cmd = Box<dyn Run>;
pub struct Command { pub struct Command {
@ -56,7 +69,7 @@ pub struct Command {
} }
impl Command { impl Command {
fn init<T>(name: T, sub_commands: Vec<Self>, f: Cmd) -> Self pub fn init<T>(name: T, sub_commands: Vec<Self>, f: Cmd) -> Self
where where
T: ToString, T: ToString,
{ {
@ -67,7 +80,7 @@ impl Command {
} }
} }
pub fn call(&self, words: Args, env: &mut Env) { pub fn call(&self, words: Args, env: &mut CmdEnv) {
let x = &self.cmd; let x = &self.cmd;
let exit_code = x(words, env); let exit_code = x(words, env);
env.set("?", &exit_code.to_string()); env.set("?", &exit_code.to_string());
@ -110,7 +123,7 @@ impl Command {
fn load_from(path: &str) -> Vec<Command> { fn load_from(path: &str) -> Vec<Command> {
let path = Path::new(path); let path = Path::new(path);
if let Ok(res) = fs::read_dir(path) { if let Ok(res) = fs::read_dir(path) {
// load all the programs from /bin // load all the programs from <path>
let bin = res.fold(Vec::new(), |mut v, contents| { let bin = res.fold(Vec::new(), |mut v, contents| {
if let Ok(cmd) = contents { if let Ok(cmd) = contents {
if let Ok(t) = cmd.file_type() { if let Ok(t) = cmd.file_type() {
@ -119,10 +132,8 @@ impl Command {
let cmd = Command::init( let cmd = Command::init(
cmd.file_name().into_string().unwrap(), cmd.file_name().into_string().unwrap(),
vec![], vec![],
Box::new(move |args: Args, env: &mut Env| { Box::new(move |args: Args, env: &mut CmdEnv| {
let name = cmd.path().to_str().unwrap().to_owned(); let name = cmd.path().to_str().unwrap().to_owned(); let out = process::Command::new(name) .args(&args[1..])
let out = process::Command::new(name)
.args(&args[1..])
.envs(env.as_env()) .envs(env.as_env())
.output() .output()
.expect("Failed to call external program"); .expect("Failed to call external program");
@ -149,7 +160,7 @@ impl Command {
Vec::new() Vec::new()
} }
pub fn all_commands(env: &Env) -> Vec<Self> { pub fn all_commands(env: &CmdEnv) -> Vec<Self> {
// load custom programs // load custom programs
let mut commands = vec![ let mut commands = vec![
Command::init( Command::init(
@ -159,46 +170,8 @@ impl Command {
std::process::exit(0); std::process::exit(0);
}), }),
), ),
Command::init( crate::cmd::echo::init(),
"echo", crate::cmd::random::init(),
vec![],
Box::new(|args, _| {
let msg = &args[1..]
.iter()
.map(|s| s.to_owned())
.intersperse(" ".to_string())
.collect::<String>();
println!("{msg}");
0
}),
),
Command::init(
"random",
vec![
Command::init(
"number",
vec![],
Box::new(|_, _| {
// how do you know it's not random?
println!("8");
0
}),
),
Command::init(
"string",
vec![],
Box::new(|_, _| {
// how do you know it's not random?
println!("hello world");
0
}),
),
],
Box::new(|args, _| {
println!("random's debug: {:?}", args);
0
}),
),
]; ];
// $PATH // $PATH

View File

@ -1,9 +1,10 @@
#![feature(iter_intersperse)] #![feature(iter_intersperse)]
#![feature(trait_alias)] #![feature(trait_alias)]
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use command::{Command, Env, Args}; use command::{Command, CmdEnv, Args};
mod command; mod command;
mod cmd;
pub static SPECIAL_CHAR: char = '$'; pub static SPECIAL_CHAR: char = '$';
@ -14,9 +15,10 @@ fn main() {
let stdout = std::io::stdout(); let stdout = std::io::stdout();
let mut out_lock = stdout.lock(); let mut out_lock = stdout.lock();
let mut env= Env::new(); let mut env= CmdEnv::new();
env.set("HOME", "/a/fake/dir"); env.set("HOME", "/tmp/fake/dir");
env.set("?", "0"); env.set("?", "0");
// env.set("PATH", "/bin/");
let commands = &Command::all_commands(&env); let commands = &Command::all_commands(&env);
println!("Loaded {} commands", commands.len()); println!("Loaded {} commands", commands.len());
@ -78,7 +80,7 @@ fn main() {
} }
} }
fn parse_lang(tokens: Args, env: &mut Env) { fn parse_lang(tokens: Args, env: &mut CmdEnv) {
if let Some(first) = tokens.first() { if let Some(first) = tokens.first() {
if let Some((var, val)) = first.split_once('=') { if let Some((var, val)) = first.split_once('=') {
let var = var.replace(SPECIAL_CHAR, ""); let var = var.replace(SPECIAL_CHAR, "");
@ -100,7 +102,7 @@ fn parse_lang(tokens: Args, env: &mut Env) {
}; };
env.set(&var, &val); env.set(&var, &val);
} else { } else {
println!("first token is not a command but also doesn't include '='"); println!("first token is not a command but also isn't setting a variable (=)");
} }
} }
} }