From 116bd4d68e19e314a96672165831ff0360e5c329 Mon Sep 17 00:00:00 2001 From: user0-07161 Date: Sun, 14 Sep 2025 19:38:20 +0200 Subject: [PATCH 1/3] feat: do error handling with anyhow, slight refactor --- Cargo.lock | 12 ++++++++++ Cargo.toml | 2 ++ coreutils/Cargo.toml | 3 +++ coreutils/src/commands/base32.rs | 7 ++++-- coreutils/src/commands/base64.rs | 7 ++++-- coreutils/src/commands/cat.rs | 9 +++++--- coreutils/src/commands/dd.rs | 36 ++++++++++++++++-------------- coreutils/src/commands/dirname.rs | 14 ++++++------ coreutils/src/commands/dos2unix.rs | 27 +++++++++++----------- coreutils/src/commands/echo.rs | 5 ++++- coreutils/src/commands/env.rs | 13 ++++++----- coreutils/src/commands/false.rs | 3 ++- coreutils/src/commands/hello.rs | 5 ++++- coreutils/src/commands/hostname.rs | 5 ++++- coreutils/src/commands/ln.rs | 25 ++++++++++++--------- coreutils/src/commands/mkdir.rs | 13 ++++++----- coreutils/src/commands/nproc.rs | 11 +++++---- coreutils/src/commands/pwd.rs | 9 +++++--- coreutils/src/commands/seq.rs | 7 ++++-- coreutils/src/commands/sleep.rs | 14 +++++++----- coreutils/src/commands/tee.rs | 11 +++++---- coreutils/src/commands/test.rs | 6 +++-- coreutils/src/commands/true.rs | 3 ++- coreutils/src/commands/unix2dos.rs | 8 ++++--- coreutils/src/commands/whoami.rs | 9 +++++--- coreutils/src/commands/yes.rs | 5 +++-- shell/Cargo.toml | 1 + shell/src/ash.rs | 25 ++++++++++----------- shell/src/built_in/cd.rs | 7 +++--- shell/src/built_in/eval.rs | 21 +++++++++-------- shell/src/built_in/exit.rs | 5 +++-- shell/src/built_in/mod.rs | 18 ++++++++------- src/boxcmd.rs | 8 +++---- src/main.rs | 7 ++++-- utils/Cargo.toml | 1 + utils/src/commands.rs | 4 +++- utils/src/registry.rs | 29 ++++++++++++++++++------ 37 files changed, 244 insertions(+), 151 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1071603..895a45d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + [[package]] name = "boxutils" version = "0.1.0" +dependencies = [ + "anyhow", +] [[package]] name = "cfg-if" @@ -16,6 +25,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "coreutils" version = "0.1.0" dependencies = [ + "anyhow", "boxutils", "hostname", "num_cpus", @@ -58,6 +68,7 @@ dependencies = [ name = "rebox" version = "0.1.0" dependencies = [ + "anyhow", "boxutils", "coreutils", "shell", @@ -67,6 +78,7 @@ dependencies = [ name = "shell" version = "0.1.0" dependencies = [ + "anyhow", "boxutils", ] diff --git a/Cargo.toml b/Cargo.toml index 3b4354e..0613e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,13 @@ resolver = "3" coreutils = { path = "./coreutils" } boxutils = { path = "./utils" } shell = { path = "./shell" } +anyhow = "1.0.99" [dependencies] coreutils.workspace = true boxutils.workspace = true shell.workspace = true +anyhow.workspace = true [[bin]] name = "box" diff --git a/coreutils/Cargo.toml b/coreutils/Cargo.toml index dbbe70e..9ac42ed 100644 --- a/coreutils/Cargo.toml +++ b/coreutils/Cargo.toml @@ -5,5 +5,8 @@ edition = "2024" [dependencies] boxutils.workspace = true +anyhow.workspace = true + +# TODO: get rid of these hostname = "0.4.0" num_cpus = "1.16.0" diff --git a/coreutils/src/commands/base32.rs b/coreutils/src/commands/base32.rs index 7764ac4..e204ee5 100644 --- a/coreutils/src/commands/base32.rs +++ b/coreutils/src/commands/base32.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use boxutils::encoding::base32; @@ -11,7 +12,7 @@ use std::io::Read; pub struct Base32; impl Command for Base32 { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-d") // decode flag .parse_args("base32"); @@ -22,7 +23,7 @@ impl Command for Base32 { let mut file: Box = match &args.get_normal_args()[..] { [] => Box::new(std::io::stdin()), [file] => Box::new(OpenOptions::new().read(true).open(file).unwrap()), - _ => panic!("base32: multiple files provided"), + _ => bail!("base32: multiple files provided"), }; let mut buffer = String::new(); @@ -35,5 +36,7 @@ impl Command for Base32 { println!("{}", data); } + + Ok(()) } } diff --git a/coreutils/src/commands/base64.rs b/coreutils/src/commands/base64.rs index ec20700..6e602aa 100644 --- a/coreutils/src/commands/base64.rs +++ b/coreutils/src/commands/base64.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use boxutils::encoding::base64; @@ -11,7 +12,7 @@ use std::io::Read; pub struct Base64; impl Command for Base64 { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-d") // decode flag .parse_args("base64"); @@ -22,7 +23,7 @@ impl Command for Base64 { let mut file: Box = match &args.get_normal_args()[..] { [] => Box::new(std::io::stdin()), [file] => Box::new(OpenOptions::new().read(true).open(file).unwrap()), - _ => panic!("base64: multiple files provided"), + _ => bail!("base64: multiple files provided"), }; let mut buffer = String::new(); @@ -35,5 +36,7 @@ impl Command for Base64 { println!("{}", data); } + + Ok(()) } } diff --git a/coreutils/src/commands/cat.rs b/coreutils/src/commands/cat.rs index d8502ff..d1ff88e 100644 --- a/coreutils/src/commands/cat.rs +++ b/coreutils/src/commands/cat.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::commands::Command; use boxutils::commands::get_args; use std::env; @@ -7,9 +8,9 @@ use std::io::{self, BufReader, Read, Write}; pub struct Cat; impl Command for Cat { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args: Vec = env::args().collect::>().clone(); - let arguments: Vec = get_args(String::from("cat"), args); + let arguments: Vec = get_args("cat".to_owned(), args); let mut vecbuf = Vec::new(); if arguments.len() == 0 { @@ -23,6 +24,8 @@ impl Command for Cat { let _ = vecbuf.append(&mut tmpbuf); } - let _ = io::stdout().write_all(&vecbuf); + io::stdout().write_all(&vecbuf)?; + + Ok(()) } } diff --git a/coreutils/src/commands/dd.rs b/coreutils/src/commands/dd.rs index 410b629..83babad 100644 --- a/coreutils/src/commands/dd.rs +++ b/coreutils/src/commands/dd.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::commands::Command; use std::collections::HashMap; use std::env; @@ -8,7 +9,7 @@ use std::time::Instant; pub struct Dd; impl Command for Dd { - fn execute(&self) { + fn execute(&self) -> Result<()> { // dd has its seperate argument parsing let mut arguments = HashMap::new(); let mut blocksize = 512; @@ -24,19 +25,18 @@ impl Command for Dd { let (k, v) = bs.split_at(bs.len() - 1); if v.parse::().is_ok() { // assume the bs is specified in bytes, - // because the last char is a number - blocksize = bs.parse::().unwrap() + // because it can be parsed as u64 + blocksize = bs.parse::()? } else { match v { - "K" | "k" => blocksize = k.parse::().unwrap() * 1024, - "M" => blocksize = k.parse::().unwrap() * 1024 * 1024, - "G" => blocksize = k.parse::().unwrap() * 1024 * 1024 * 1024, - "kB" => blocksize = k.parse::().unwrap() * 1000, - "MB" => blocksize = k.parse::().unwrap() * 1000 * 1000, - "GB" => blocksize = k.parse::().unwrap() * 1000 * 1000 * 1000, + "K" | "k" => blocksize = k.parse::()? * 1024, + "M" => blocksize = k.parse::()? * 1024 * 1024, + "G" => blocksize = k.parse::()? * 1024 * 1024 * 1024, + "kB" => blocksize = k.parse::()? * 1000, + "MB" => blocksize = k.parse::()? * 1000 * 1000, + "GB" => blocksize = k.parse::()? * 1000 * 1000 * 1000, _ => { - eprintln!("Invalid blocksize specified."); - return; + bail!("Invalid blocksize specified."); } } } @@ -46,17 +46,17 @@ impl Command for Dd { let start = Instant::now(); if let Some(input) = arguments.get("if") { - let mut f = BufReader::new(File::open(input).unwrap()); - let _ = f.read_to_end(&mut vecbuf).unwrap(); + let mut f = BufReader::new(File::open(input)?); + f.read_to_end(&mut vecbuf)?; } else { - let _ = io::stdin().read_to_end(&mut vecbuf).unwrap(); + io::stdin().read_to_end(&mut vecbuf)?; } if let Some(output) = arguments.get("of") { let buffer = File::create(output); - let _ = buffer.unwrap().write(&vecbuf); + buffer.unwrap().write(&vecbuf)?; } else { - let _ = io::stdout().write_all(&vecbuf); + io::stdout().write_all(&vecbuf)?; } let duration = start.elapsed().as_secs_f64(); @@ -77,6 +77,8 @@ impl Command for Dd { vecbuf.len(), duration, kb_per_sec - ) + ); + + Ok(()) } } diff --git a/coreutils/src/commands/dirname.rs b/coreutils/src/commands/dirname.rs index 8fd18d3..0dcb38f 100644 --- a/coreutils/src/commands/dirname.rs +++ b/coreutils/src/commands/dirname.rs @@ -1,18 +1,16 @@ -use std::path::Path; - +use anyhow::{Result, bail}; use boxutils::{args::ArgParser, commands::Command}; - +use std::path::Path; pub struct Dirname; impl Command for Dirname { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .parse_args("dirname"); if args.get_normal_args().len() != 1 || args.get_flag("--help") { - println!("Usage: dirname FILENAME"); - return; + bail!("Usage: dirname FILENAME"); } // note(teesh): we have already checked for argnums, so we're fine :D @@ -30,7 +28,9 @@ impl Command for Dirname { println!("{}", to_print); } else { - println!("dirname: could not get parent") + bail!("dirname: could not get parent") } + + Ok(()) } } diff --git a/coreutils/src/commands/dos2unix.rs b/coreutils/src/commands/dos2unix.rs index 2a73912..e36aa3f 100644 --- a/coreutils/src/commands/dos2unix.rs +++ b/coreutils/src/commands/dos2unix.rs @@ -1,8 +1,8 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs::File; use std::io::{self, BufReader, Read, Write}; -use std::process::exit; pub fn convert(arguments: &ArgParser, d2u: bool) -> Vec { let mut vecbuf = Vec::new(); @@ -19,10 +19,7 @@ pub fn convert(arguments: &ArgParser, d2u: bool) -> Vec { } if d2u { - vecbuf.retain( - |x| - *x != b'\r' - ); + vecbuf.retain(|x| *x != b'\r'); } else { let mut tmpbuf = Vec::new(); vecbuf.iter().enumerate().for_each(|(i, &b)| { @@ -40,7 +37,7 @@ pub fn convert(arguments: &ArgParser, d2u: bool) -> Vec { pub struct Dos2Unix; impl Command for Dos2Unix { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-u") .add_flag("-d") @@ -58,19 +55,23 @@ impl Command for Dos2Unix { } if args.get_flag("--help") { - println!("Usage: dos2unix [-d] [-u] [FILE]"); - print!("\n"); - println!("-d: unix2dos"); - println!("-u: dos2unix (default)"); - exit(0); + let help_text = "\ +Usage: dos2unix [-d] [-u] [FILE] + +-d: unix2dos +-u: dos2unix (default)\ +"; + bail!("{}", help_text) } let result = convert(&args, dos2unix); if args.get_normal_args().len() < 1 { - let _ = io::stdout().write_all(&result); + io::stdout().write_all(&result)?; } else { - let _ = std::fs::write(args.get_normal_args()[0].clone(), &result); + std::fs::write(args.get_normal_args()[0].clone(), &result)?; } + + Ok(()) } } diff --git a/coreutils/src/commands/echo.rs b/coreutils/src/commands/echo.rs index aa96efc..77f92a6 100644 --- a/coreutils/src/commands/echo.rs +++ b/coreutils/src/commands/echo.rs @@ -1,12 +1,15 @@ +use anyhow::Result; use boxutils::commands::Command; pub struct Echo; impl Command for Echo { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args: Vec = std::env::args().collect::>(); let arguments: Vec = boxutils::commands::get_args(String::from("echo"), args); let message = &arguments.join(" "); println!("{}", message); + + Ok(()) } } diff --git a/coreutils/src/commands/env.rs b/coreutils/src/commands/env.rs index 11de7ab..f6672b2 100644 --- a/coreutils/src/commands/env.rs +++ b/coreutils/src/commands/env.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; @@ -35,7 +36,7 @@ impl Env { } impl Command for Env { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-i") .add_flag("-0") @@ -59,20 +60,20 @@ impl Command for Env { if to_run.is_empty() { Env::print_all_vars(); - return; + return Ok(()); } if let Some((command, args)) = to_run.split_first() { std::process::Command::new(command) .args(args) - .spawn() - .expect("env: failed to start command") - .wait() - .expect("env: could not wait for command"); + .spawn()? + .wait()?; } if null_terminate { print!("\0"); } + + Ok(()) } } diff --git a/coreutils/src/commands/false.rs b/coreutils/src/commands/false.rs index d162e34..a66a0ee 100644 --- a/coreutils/src/commands/false.rs +++ b/coreutils/src/commands/false.rs @@ -1,10 +1,11 @@ +use anyhow::Result; use boxutils::commands::Command; use std::process::exit; pub struct False; impl Command for False { - fn execute(&self) { + fn execute(&self) -> Result<()> { exit(1); } } diff --git a/coreutils/src/commands/hello.rs b/coreutils/src/commands/hello.rs index e6763a5..d483736 100644 --- a/coreutils/src/commands/hello.rs +++ b/coreutils/src/commands/hello.rs @@ -1,9 +1,12 @@ +use anyhow::Result; use boxutils::commands::Command; pub struct Hello; impl Command for Hello { - fn execute(&self) { + fn execute(&self) -> Result<()> { println!("Hello, world!"); + + Ok(()) } } diff --git a/coreutils/src/commands/hostname.rs b/coreutils/src/commands/hostname.rs index da0826d..b4a15f3 100644 --- a/coreutils/src/commands/hostname.rs +++ b/coreutils/src/commands/hostname.rs @@ -1,10 +1,11 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Hostname; impl Command for Hostname { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .parse_args("hostname"); @@ -19,5 +20,7 @@ impl Command for Hostname { }; println!("{}", hostname.to_string_lossy()); + + Ok(()) } } diff --git a/coreutils/src/commands/ln.rs b/coreutils/src/commands/ln.rs index b851c3e..fbd123f 100644 --- a/coreutils/src/commands/ln.rs +++ b/coreutils/src/commands/ln.rs @@ -1,11 +1,11 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs; - pub struct Ln; impl Command for Ln { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .add_flag("-s") @@ -20,7 +20,7 @@ impl Command for Ln { let help = args.get_normal_args().len() != 2 || args.get_flag("--help"); if help { println!("Usage: ln [-sfnbtv] [-S SUF] TARGET LINK"); - return; + return Ok(()); } let to_be_linked = args.get_normal_args()[0].clone(); @@ -31,11 +31,11 @@ impl Command for Ln { } if args.get_flag("-f") { - if fs::exists(&destination).unwrap() { - if fs::metadata(&destination).unwrap().is_dir() { - fs::remove_dir_all(&destination).unwrap(); + if fs::exists(&destination)? { + if fs::metadata(&destination)?.is_dir() { + fs::remove_dir_all(&destination)?; } else { - fs::remove_file(&destination).unwrap(); + fs::remove_file(&destination)?; } } } @@ -47,19 +47,22 @@ impl Command for Ln { if args.get_flag("-b") { let suffix = args.get_option("-S").unwrap_or("~"); let new_filename = format!("{}{}", destination, suffix); - let _ = fs::rename(&destination, &new_filename); + fs::rename(&destination, &new_filename)?; } if args.get_flag("-s") { - let symlink_result = boxutils::cross::fs::symlink(to_be_linked, destination, dereference); + let symlink_result = + boxutils::cross::fs::symlink(to_be_linked, destination, dereference); if let Err(e) = symlink_result { - eprintln!("ln: failed to create symlink: {}", e); + bail!("ln: failed to create symlink: {}", e); } } else { if let Err(e) = boxutils::cross::fs::hard_link(to_be_linked, destination, dereference) { - eprintln!("ln: failed to create hard link: {}", e); + bail!("ln: failed to create hard link: {}", e); } } + + Ok(()) } } diff --git a/coreutils/src/commands/mkdir.rs b/coreutils/src/commands/mkdir.rs index f2f2371..8e24f65 100644 --- a/coreutils/src/commands/mkdir.rs +++ b/coreutils/src/commands/mkdir.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs; @@ -5,7 +6,7 @@ use std::fs; pub struct Mkdir; impl Command for Mkdir { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-p") .add_flag("--parents") @@ -14,11 +15,11 @@ impl Command for Mkdir { if args.get_flag("--help") { println!("Usage: mkdir [DIR1] [DIR2] etc. pp. [-p, --parents]"); - return; + return Ok(()); } if args.get_normal_args().len() == 0 { - panic!("Usage: mkdir [DIR1] [DIR1] etc. pp. [-p, --parents]"); + bail!("Usage: mkdir [DIR1] [DIR1] etc. pp. [-p, --parents]"); } let parented = args.get_flag("-p") || args.get_flag("--parents"); @@ -27,10 +28,12 @@ impl Command for Mkdir { for dir in to_create { if parented { - let _ = fs::create_dir_all(dir); + fs::create_dir_all(dir)?; } else { - let _ = fs::create_dir(dir); + fs::create_dir(dir)?; } } + + Ok(()) } } diff --git a/coreutils/src/commands/nproc.rs b/coreutils/src/commands/nproc.rs index f0bedd3..3dfaa44 100644 --- a/coreutils/src/commands/nproc.rs +++ b/coreutils/src/commands/nproc.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; use num_cpus::get; @@ -5,7 +6,7 @@ use num_cpus::get; pub struct Nproc; impl Command for Nproc { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .add_flag("--ignore") @@ -23,7 +24,7 @@ Prints the number of available CPUs to stdout. --ignore=N, --ignore N Ignore N CPUs " ); - return; + return Ok(()); } if args.get_flag("--all") { @@ -31,13 +32,13 @@ Prints the number of available CPUs to stdout. } if args.get_flag("--ignore") { - ignore = args.get_option("--ignore").unwrap().parse().unwrap(); + ignore = args.get_option("--ignore").unwrap().parse()?; } for argument in args.get_normal_args() { if let Some((k, v)) = argument.split_once('=') { if k == "--ignore" { - ignore = v.parse().unwrap(); + ignore = v.parse()?; } } } @@ -47,5 +48,7 @@ Prints the number of available CPUs to stdout. } else { println!("{}", get() as u64 - ignore) } + + Ok(()) } } diff --git a/coreutils/src/commands/pwd.rs b/coreutils/src/commands/pwd.rs index c2f54ed..f879b3a 100644 --- a/coreutils/src/commands/pwd.rs +++ b/coreutils/src/commands/pwd.rs @@ -1,18 +1,21 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Pwd; impl Command for Pwd { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().add_flag("--help").parse_args("yes"); if args.get_flag("--help") { println!("Usage: pwd"); - return; + return Ok(()); } - let pwd = std::env::current_dir().unwrap(); + let pwd = std::env::current_dir()?; println!("{}", pwd.display()); + + Ok(()) } } diff --git a/coreutils/src/commands/seq.rs b/coreutils/src/commands/seq.rs index 13c6694..075dc81 100644 --- a/coreutils/src/commands/seq.rs +++ b/coreutils/src/commands/seq.rs @@ -1,9 +1,10 @@ +use anyhow::{Result, bail}; use boxutils::{args::ArgParser, commands::Command}; pub struct Seq; impl Command for Seq { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_option("-s") .add_flag("-w") @@ -31,7 +32,7 @@ impl Command for Seq { incnum = inc.parse().expect("seq: invalid inc number"); lastnum = last.parse().expect("seq: invalid last number"); } - _ => panic!("seq: malformed arguments"), + _ => bail!("seq: malformed arguments"), } let mut accumulator = firstnum; @@ -62,5 +63,7 @@ impl Command for Seq { print!("{}", separator); } } + + Ok(()) } } diff --git a/coreutils/src/commands/sleep.rs b/coreutils/src/commands/sleep.rs index 413c545..4f5fe60 100644 --- a/coreutils/src/commands/sleep.rs +++ b/coreutils/src/commands/sleep.rs @@ -1,15 +1,16 @@ +use anyhow::{Result, bail}; use boxutils::{args::ArgParser, commands::Command}; pub struct Sleep; impl Command for Sleep { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().parse_args("sleep"); let mut sleep_for = 0; for arg in args.get_normal_args() { if arg.chars().last().unwrap().is_numeric() { - sleep_for += arg.parse::().unwrap(); + sleep_for += arg.parse::()?; } else { let multiplier = match arg.chars().last().unwrap() { 's' => 1, @@ -17,20 +18,21 @@ impl Command for Sleep { 'h' => 3600, 'd' => 86400, _ => { - println!("Invalid time interval '{}'", arg); - return; + bail!("Invalid time interval '{}'", arg); } }; - sleep_for += arg[..arg.len() - 1].parse::().unwrap() * multiplier; + sleep_for += arg[..arg.len() - 1].parse::()? * multiplier; } } if sleep_for == 0 { println!("Usage: sleep [N]..."); - return; + return Ok(()); } std::thread::sleep(std::time::Duration::from_secs(sleep_for as u64)); + + Ok(()) } } diff --git a/coreutils/src/commands/tee.rs b/coreutils/src/commands/tee.rs index 46bbe71..c8b8672 100644 --- a/coreutils/src/commands/tee.rs +++ b/coreutils/src/commands/tee.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs::OpenOptions; @@ -10,7 +11,7 @@ use std::io::Write; pub struct Tee; impl Command for Tee { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .add_flag("-a") @@ -34,17 +35,19 @@ impl Command for Tee { if let Ok(this_file) = this_file { writes.push(Box::new(this_file)); } else { - eprintln!("tee: unable to open file: {}", file); + bail!("tee: unable to open file: {}", file); } } let mut buffer = String::new(); while boxutils::input::repl(&mut buffer) { for output in &mut writes { - let _ = output.write_all(buffer.as_bytes()); - let _ = output.flush(); + output.write_all(buffer.as_bytes())?; + output.flush()?; } buffer.clear(); } + + Ok(()) } } diff --git a/coreutils/src/commands/test.rs b/coreutils/src/commands/test.rs index 2de7ea6..2959acb 100644 --- a/coreutils/src/commands/test.rs +++ b/coreutils/src/commands/test.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; @@ -194,7 +195,7 @@ impl Test { } impl Command for Test { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flags(STRING_TESTS.into()) .add_flags(NUMBER_TESTS.into()) @@ -202,7 +203,7 @@ impl Command for Test { .parse_args(if self.bracket { "[" } else { "test" }); if self.bracket && !args.get_flag("]") { - panic!("[: missing ]"); + bail!("[: missing ]"); } if STRING_TESTS.iter().any(|&x| args.get_flag(x)) { @@ -214,5 +215,6 @@ impl Command for Test { } exit_false(); + unreachable!(); } } diff --git a/coreutils/src/commands/true.rs b/coreutils/src/commands/true.rs index 3dcaf9c..2ceb189 100644 --- a/coreutils/src/commands/true.rs +++ b/coreutils/src/commands/true.rs @@ -1,10 +1,11 @@ +use anyhow::Result; use boxutils::commands::Command; use std::process::exit; pub struct True; impl Command for True { - fn execute(&self) { + fn execute(&self) -> Result<()> { exit(0); } } diff --git a/coreutils/src/commands/unix2dos.rs b/coreutils/src/commands/unix2dos.rs index cccbef6..98d212e 100644 --- a/coreutils/src/commands/unix2dos.rs +++ b/coreutils/src/commands/unix2dos.rs @@ -1,4 +1,5 @@ use crate::commands::dos2unix::convert; +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::io::{self, Write}; @@ -7,7 +8,7 @@ use std::process::exit; pub struct Unix2Dos; impl Command for Unix2Dos { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-u") .add_flag("-d") @@ -35,10 +36,11 @@ impl Command for Unix2Dos { let result = convert(&args, dos2unix); if args.get_normal_args().len() < 1 { - let _ = io::stdout().write_all(&result); + io::stdout().write_all(&result)?; } else { - let _ = std::fs::write(args.get_normal_args()[0].clone(), &result); + std::fs::write(args.get_normal_args()[0].clone(), &result)?; } + Ok(()) } } diff --git a/coreutils/src/commands/whoami.rs b/coreutils/src/commands/whoami.rs index 0f501da..ee322bc 100644 --- a/coreutils/src/commands/whoami.rs +++ b/coreutils/src/commands/whoami.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; use boxutils::cross::user; @@ -5,16 +6,18 @@ use boxutils::cross::user; pub struct WhoAmI; impl Command for WhoAmI { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().add_flag("--help").parse_args("whoami"); if args.get_flag("--help") { println!("Usage: whoami"); - return; + return Ok(()); } - let username = user::get_username().unwrap_or_else(|| "unknown".to_string()); + let username = user::get_username().unwrap_or("unknown".to_string()); println!("{}", username); + + Ok(()) } } diff --git a/coreutils/src/commands/yes.rs b/coreutils/src/commands/yes.rs index 14df289..edad407 100644 --- a/coreutils/src/commands/yes.rs +++ b/coreutils/src/commands/yes.rs @@ -1,15 +1,16 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Yes; impl Command for Yes { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().add_flag("--help").parse_args("yes"); if args.get_flag("--help") { println!("Usage: yes [STRING]"); - return; + return Ok(()); } let string = if args.get_normal_args().is_empty() { diff --git a/shell/Cargo.toml b/shell/Cargo.toml index 2bdf0a5..799dc24 100644 --- a/shell/Cargo.toml +++ b/shell/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] boxutils.workspace = true +anyhow.workspace = true diff --git a/shell/src/ash.rs b/shell/src/ash.rs index 4153e2f..c874177 100644 --- a/shell/src/ash.rs +++ b/shell/src/ash.rs @@ -1,14 +1,17 @@ use boxutils::commands::Command; + use std::env; use std::io::{self, Write}; use std::process; +use anyhow::{Error, Result}; + use crate::built_in::{Action, run_if_exists}; pub struct Ash; impl Command for Ash { - fn execute(&self) { + fn execute(&self) -> Result<(), Error> { let mut path = env::current_dir().unwrap().display().to_string(); loop { @@ -20,13 +23,13 @@ impl Command for Ash { print!("{} {} ", path.clone(), userchar); let _ = io::stdout().flush(); let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); + io::stdin().read_line(&mut input)?; let mut full_cmd = input.trim().split_whitespace(); - let command = full_cmd.next().unwrap_or(" "); + let command = full_cmd.next().unwrap_or(""); let arguments: Vec<&str> = full_cmd.collect(); if let Some(action) = run_if_exists(command.to_string(), arguments.clone()) { - match action { + match action? { Action::Exit => { break; } @@ -39,17 +42,13 @@ impl Command for Ash { Action::Nothing => {} } } else { - let out = process::Command::new(command) + process::Command::new(command) .args(arguments.clone()) - .spawn(); - - match out { - Ok(mut out) => { - let _ = out.wait(); - } - Err(err) => println!("{:?}", err), - } + .spawn()? + .wait()?; } } + + Ok(()) } } diff --git a/shell/src/built_in/cd.rs b/shell/src/built_in/cd.rs index c20adfc..e4a8df5 100644 --- a/shell/src/built_in/cd.rs +++ b/shell/src/built_in/cd.rs @@ -1,9 +1,10 @@ use crate::built_in::Action; +use anyhow::{Result, bail}; -pub fn cd(arguments: Vec<&str>) -> Action { +pub fn cd(arguments: Vec<&str>) -> Result { if arguments.len() < 1 { - panic!("cd expects **one** argument"); + bail!("cd expects **one** argument"); } - Action::ChangeDirectory(arguments[0].to_owned().clone()) + Ok(Action::ChangeDirectory(arguments[0].to_owned().clone())) } diff --git a/shell/src/built_in/eval.rs b/shell/src/built_in/eval.rs index 203b0cc..efa600d 100644 --- a/shell/src/built_in/eval.rs +++ b/shell/src/built_in/eval.rs @@ -1,17 +1,16 @@ use crate::built_in::Action; +use anyhow::{Result, bail}; use std::process::Command; -pub fn eval(arguments: Vec<&str>) -> Action { +pub fn eval(arguments: Vec<&str>) -> Result { if arguments.len() < 1 { - panic!("eval expects **one or more** arguments"); + bail!("eval expects **one or more** arguments"); } - let output = Command::new(arguments[0]).args(&arguments[1..]).spawn(); - match output { - Ok(mut output) => { - output.wait().expect("failed to wait for process exit"); - } - Err(err) => println!("{:?}", err) - } - Action::Nothing -} \ No newline at end of file + Command::new(arguments[0]) + .args(&arguments[1..]) + .spawn()? + .wait()?; + Ok(Action::Nothing) +} + diff --git a/shell/src/built_in/exit.rs b/shell/src/built_in/exit.rs index c0e6922..f6298b4 100644 --- a/shell/src/built_in/exit.rs +++ b/shell/src/built_in/exit.rs @@ -1,5 +1,6 @@ use crate::built_in::Action; +use anyhow::Result; -pub fn exit(_: Vec<&str>) -> Action { - Action::Exit +pub fn exit(_: Vec<&str>) -> Result { + Ok(Action::Exit) } diff --git a/shell/src/built_in/mod.rs b/shell/src/built_in/mod.rs index 47e5c73..1906a68 100644 --- a/shell/src/built_in/mod.rs +++ b/shell/src/built_in/mod.rs @@ -1,21 +1,23 @@ mod cd; -mod exit; mod eval; +mod exit; + +use anyhow::Result; #[derive(Debug)] pub enum Action { Exit, ChangeDirectory(String), - Nothing + Nothing, } -fn get_function(command: String) -> Option) -> Action> { +fn get_function(command: String) -> Option) -> Result> { let registry = [ - ("exit", exit::exit as fn(Vec<&str>) -> Action), - ("cd", cd::cd as fn(Vec<&str>) -> Action), - ("eval", eval::eval as fn(Vec<&str>) -> Action) + ("exit", exit::exit as fn(Vec<&str>) -> Result), + ("cd", cd::cd as fn(Vec<&str>) -> Result), + ("eval", eval::eval as fn(Vec<&str>) -> Result), ]; - let mut function: Option) -> Action> = None; + let mut function: Option) -> Result> = None; for entry in registry { if entry.0 == &command { @@ -26,7 +28,7 @@ fn get_function(command: String) -> Option) -> Action> { function } -pub fn run_if_exists(command: String, arguments: Vec<&str>) -> Option { +pub fn run_if_exists(command: String, arguments: Vec<&str>) -> Option> { let function = get_function(command); if let Some(function) = function { let action = function(arguments); diff --git a/src/boxcmd.rs b/src/boxcmd.rs index 6ddbb6a..b2334d6 100644 --- a/src/boxcmd.rs +++ b/src/boxcmd.rs @@ -1,11 +1,12 @@ use super::registry::get_registry; +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Boxcmd; impl Command for Boxcmd { - fn execute(&self) { + fn execute(&self) -> Result<()> { let parser = ArgParser::builder().parse_args("box"); let registry = get_registry(); @@ -15,11 +16,10 @@ impl Command for Boxcmd { continue; } - registry.execute(&command); - return; + registry.execute(&command)?; } - println!( + bail!( "No valid command provided. Included commands:\n{}", registry.list().join(", ") ); diff --git a/src/main.rs b/src/main.rs index 343fc74..3a0959b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ mod boxcmd; mod registry; +use anyhow::{Error, Result}; use boxutils::commands::being_called_as; -fn main() { +fn main() -> Result<(), Error> { let utility_name = being_called_as(); - registry::get_registry().execute(&utility_name); + registry::get_registry().execute(&utility_name)?; + + Ok(()) } diff --git a/utils/Cargo.toml b/utils/Cargo.toml index c6d1cb8..63a4b2a 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow.workspace = true diff --git a/utils/src/commands.rs b/utils/src/commands.rs index 5d3ad17..837c802 100644 --- a/utils/src/commands.rs +++ b/utils/src/commands.rs @@ -2,6 +2,8 @@ use std::env; use std::mem; use std::path::Path; +use anyhow::{Error, Result}; + pub fn being_called_as() -> String { let args = env::args().collect::>(); let exe_path = args[0].clone(); @@ -34,5 +36,5 @@ pub fn get_args(commandname: String, args: Vec) -> Vec { } pub trait Command { - fn execute(&self); + fn execute(&self) -> Result<(), Error>; } diff --git a/utils/src/registry.rs b/utils/src/registry.rs index 4ee4e0d..61a3480 100644 --- a/utils/src/registry.rs +++ b/utils/src/registry.rs @@ -1,6 +1,8 @@ use super::commands::Command; use std::collections::HashMap; +use anyhow::{Error, Result, bail}; + pub struct CommandRegistry { commands: HashMap>, } @@ -28,23 +30,31 @@ impl CommandRegistry { self.commands.get(name) } - pub fn execute(&self, name: &str) { + pub fn execute(&self, name: &str) -> Result<(), Error> { if let Some(command) = self.get(name) { - command.execute(); + command.execute()?; } else { - println!("Command not found: {}", name); + bail!("Command not found: {}", name); } + + Ok(()) } } mod tests { + #[allow(unused_imports)] // why the heck is rust saying it's unused?? + use anyhow::{Error, Result}; + #[test] fn test_register() { use super::Command; use super::CommandRegistry; + struct TestCommand; impl Command for TestCommand { - fn execute(&self) {} + fn execute(&self) -> Result<(), Error> { + Ok(()) + } } let mut registry = CommandRegistry::new(); @@ -53,18 +63,23 @@ mod tests { } #[test] - fn test_execute() { + fn test_execute() -> Result<(), Error> { use super::Command; use super::CommandRegistry; + struct TestCommand; impl Command for TestCommand { - fn execute(&self) { + fn execute(&self) -> Result<(), Error> { println!("TestCommand executed"); + + Ok(()) } } let mut registry = CommandRegistry::new(); registry.register("test", Box::new(TestCommand)); - registry.execute("test"); + registry.execute("test")?; + + Ok(()) } } From 0cd7b3af1238b848fbe6310bb8654d5fa5082dc5 Mon Sep 17 00:00:00 2001 From: User0 <104380885+user0-07161@users.noreply.github.com> Date: Sun, 14 Sep 2025 19:42:10 +0200 Subject: [PATCH 2/3] feat: do error handling with anyhow, slight refactor (#6) Co-authored-by: user0-07161 --- Cargo.lock | 12 ++++++++++ Cargo.toml | 2 ++ coreutils/Cargo.toml | 3 +++ coreutils/src/commands/base32.rs | 7 ++++-- coreutils/src/commands/base64.rs | 7 ++++-- coreutils/src/commands/cat.rs | 9 +++++--- coreutils/src/commands/dd.rs | 36 ++++++++++++++++-------------- coreutils/src/commands/dirname.rs | 14 ++++++------ coreutils/src/commands/dos2unix.rs | 27 +++++++++++----------- coreutils/src/commands/echo.rs | 5 ++++- coreutils/src/commands/env.rs | 13 ++++++----- coreutils/src/commands/false.rs | 3 ++- coreutils/src/commands/hello.rs | 5 ++++- coreutils/src/commands/hostname.rs | 5 ++++- coreutils/src/commands/ln.rs | 25 ++++++++++++--------- coreutils/src/commands/mkdir.rs | 13 ++++++----- coreutils/src/commands/nproc.rs | 11 +++++---- coreutils/src/commands/pwd.rs | 9 +++++--- coreutils/src/commands/seq.rs | 7 ++++-- coreutils/src/commands/sleep.rs | 14 +++++++----- coreutils/src/commands/tee.rs | 11 +++++---- coreutils/src/commands/test.rs | 6 +++-- coreutils/src/commands/true.rs | 3 ++- coreutils/src/commands/unix2dos.rs | 8 ++++--- coreutils/src/commands/whoami.rs | 9 +++++--- coreutils/src/commands/yes.rs | 5 +++-- shell/Cargo.toml | 1 + shell/src/ash.rs | 25 ++++++++++----------- shell/src/built_in/cd.rs | 7 +++--- shell/src/built_in/eval.rs | 21 +++++++++-------- shell/src/built_in/exit.rs | 5 +++-- shell/src/built_in/mod.rs | 18 ++++++++------- src/boxcmd.rs | 8 +++---- src/main.rs | 7 ++++-- utils/Cargo.toml | 1 + utils/src/commands.rs | 4 +++- utils/src/registry.rs | 29 ++++++++++++++++++------ 37 files changed, 244 insertions(+), 151 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1071603..895a45d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + [[package]] name = "boxutils" version = "0.1.0" +dependencies = [ + "anyhow", +] [[package]] name = "cfg-if" @@ -16,6 +25,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "coreutils" version = "0.1.0" dependencies = [ + "anyhow", "boxutils", "hostname", "num_cpus", @@ -58,6 +68,7 @@ dependencies = [ name = "rebox" version = "0.1.0" dependencies = [ + "anyhow", "boxutils", "coreutils", "shell", @@ -67,6 +78,7 @@ dependencies = [ name = "shell" version = "0.1.0" dependencies = [ + "anyhow", "boxutils", ] diff --git a/Cargo.toml b/Cargo.toml index 3b4354e..0613e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,13 @@ resolver = "3" coreutils = { path = "./coreutils" } boxutils = { path = "./utils" } shell = { path = "./shell" } +anyhow = "1.0.99" [dependencies] coreutils.workspace = true boxutils.workspace = true shell.workspace = true +anyhow.workspace = true [[bin]] name = "box" diff --git a/coreutils/Cargo.toml b/coreutils/Cargo.toml index dbbe70e..9ac42ed 100644 --- a/coreutils/Cargo.toml +++ b/coreutils/Cargo.toml @@ -5,5 +5,8 @@ edition = "2024" [dependencies] boxutils.workspace = true +anyhow.workspace = true + +# TODO: get rid of these hostname = "0.4.0" num_cpus = "1.16.0" diff --git a/coreutils/src/commands/base32.rs b/coreutils/src/commands/base32.rs index 7764ac4..e204ee5 100644 --- a/coreutils/src/commands/base32.rs +++ b/coreutils/src/commands/base32.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use boxutils::encoding::base32; @@ -11,7 +12,7 @@ use std::io::Read; pub struct Base32; impl Command for Base32 { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-d") // decode flag .parse_args("base32"); @@ -22,7 +23,7 @@ impl Command for Base32 { let mut file: Box = match &args.get_normal_args()[..] { [] => Box::new(std::io::stdin()), [file] => Box::new(OpenOptions::new().read(true).open(file).unwrap()), - _ => panic!("base32: multiple files provided"), + _ => bail!("base32: multiple files provided"), }; let mut buffer = String::new(); @@ -35,5 +36,7 @@ impl Command for Base32 { println!("{}", data); } + + Ok(()) } } diff --git a/coreutils/src/commands/base64.rs b/coreutils/src/commands/base64.rs index ec20700..6e602aa 100644 --- a/coreutils/src/commands/base64.rs +++ b/coreutils/src/commands/base64.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use boxutils::encoding::base64; @@ -11,7 +12,7 @@ use std::io::Read; pub struct Base64; impl Command for Base64 { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-d") // decode flag .parse_args("base64"); @@ -22,7 +23,7 @@ impl Command for Base64 { let mut file: Box = match &args.get_normal_args()[..] { [] => Box::new(std::io::stdin()), [file] => Box::new(OpenOptions::new().read(true).open(file).unwrap()), - _ => panic!("base64: multiple files provided"), + _ => bail!("base64: multiple files provided"), }; let mut buffer = String::new(); @@ -35,5 +36,7 @@ impl Command for Base64 { println!("{}", data); } + + Ok(()) } } diff --git a/coreutils/src/commands/cat.rs b/coreutils/src/commands/cat.rs index d8502ff..d1ff88e 100644 --- a/coreutils/src/commands/cat.rs +++ b/coreutils/src/commands/cat.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::commands::Command; use boxutils::commands::get_args; use std::env; @@ -7,9 +8,9 @@ use std::io::{self, BufReader, Read, Write}; pub struct Cat; impl Command for Cat { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args: Vec = env::args().collect::>().clone(); - let arguments: Vec = get_args(String::from("cat"), args); + let arguments: Vec = get_args("cat".to_owned(), args); let mut vecbuf = Vec::new(); if arguments.len() == 0 { @@ -23,6 +24,8 @@ impl Command for Cat { let _ = vecbuf.append(&mut tmpbuf); } - let _ = io::stdout().write_all(&vecbuf); + io::stdout().write_all(&vecbuf)?; + + Ok(()) } } diff --git a/coreutils/src/commands/dd.rs b/coreutils/src/commands/dd.rs index 410b629..83babad 100644 --- a/coreutils/src/commands/dd.rs +++ b/coreutils/src/commands/dd.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::commands::Command; use std::collections::HashMap; use std::env; @@ -8,7 +9,7 @@ use std::time::Instant; pub struct Dd; impl Command for Dd { - fn execute(&self) { + fn execute(&self) -> Result<()> { // dd has its seperate argument parsing let mut arguments = HashMap::new(); let mut blocksize = 512; @@ -24,19 +25,18 @@ impl Command for Dd { let (k, v) = bs.split_at(bs.len() - 1); if v.parse::().is_ok() { // assume the bs is specified in bytes, - // because the last char is a number - blocksize = bs.parse::().unwrap() + // because it can be parsed as u64 + blocksize = bs.parse::()? } else { match v { - "K" | "k" => blocksize = k.parse::().unwrap() * 1024, - "M" => blocksize = k.parse::().unwrap() * 1024 * 1024, - "G" => blocksize = k.parse::().unwrap() * 1024 * 1024 * 1024, - "kB" => blocksize = k.parse::().unwrap() * 1000, - "MB" => blocksize = k.parse::().unwrap() * 1000 * 1000, - "GB" => blocksize = k.parse::().unwrap() * 1000 * 1000 * 1000, + "K" | "k" => blocksize = k.parse::()? * 1024, + "M" => blocksize = k.parse::()? * 1024 * 1024, + "G" => blocksize = k.parse::()? * 1024 * 1024 * 1024, + "kB" => blocksize = k.parse::()? * 1000, + "MB" => blocksize = k.parse::()? * 1000 * 1000, + "GB" => blocksize = k.parse::()? * 1000 * 1000 * 1000, _ => { - eprintln!("Invalid blocksize specified."); - return; + bail!("Invalid blocksize specified."); } } } @@ -46,17 +46,17 @@ impl Command for Dd { let start = Instant::now(); if let Some(input) = arguments.get("if") { - let mut f = BufReader::new(File::open(input).unwrap()); - let _ = f.read_to_end(&mut vecbuf).unwrap(); + let mut f = BufReader::new(File::open(input)?); + f.read_to_end(&mut vecbuf)?; } else { - let _ = io::stdin().read_to_end(&mut vecbuf).unwrap(); + io::stdin().read_to_end(&mut vecbuf)?; } if let Some(output) = arguments.get("of") { let buffer = File::create(output); - let _ = buffer.unwrap().write(&vecbuf); + buffer.unwrap().write(&vecbuf)?; } else { - let _ = io::stdout().write_all(&vecbuf); + io::stdout().write_all(&vecbuf)?; } let duration = start.elapsed().as_secs_f64(); @@ -77,6 +77,8 @@ impl Command for Dd { vecbuf.len(), duration, kb_per_sec - ) + ); + + Ok(()) } } diff --git a/coreutils/src/commands/dirname.rs b/coreutils/src/commands/dirname.rs index 8fd18d3..0dcb38f 100644 --- a/coreutils/src/commands/dirname.rs +++ b/coreutils/src/commands/dirname.rs @@ -1,18 +1,16 @@ -use std::path::Path; - +use anyhow::{Result, bail}; use boxutils::{args::ArgParser, commands::Command}; - +use std::path::Path; pub struct Dirname; impl Command for Dirname { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .parse_args("dirname"); if args.get_normal_args().len() != 1 || args.get_flag("--help") { - println!("Usage: dirname FILENAME"); - return; + bail!("Usage: dirname FILENAME"); } // note(teesh): we have already checked for argnums, so we're fine :D @@ -30,7 +28,9 @@ impl Command for Dirname { println!("{}", to_print); } else { - println!("dirname: could not get parent") + bail!("dirname: could not get parent") } + + Ok(()) } } diff --git a/coreutils/src/commands/dos2unix.rs b/coreutils/src/commands/dos2unix.rs index 2a73912..e36aa3f 100644 --- a/coreutils/src/commands/dos2unix.rs +++ b/coreutils/src/commands/dos2unix.rs @@ -1,8 +1,8 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs::File; use std::io::{self, BufReader, Read, Write}; -use std::process::exit; pub fn convert(arguments: &ArgParser, d2u: bool) -> Vec { let mut vecbuf = Vec::new(); @@ -19,10 +19,7 @@ pub fn convert(arguments: &ArgParser, d2u: bool) -> Vec { } if d2u { - vecbuf.retain( - |x| - *x != b'\r' - ); + vecbuf.retain(|x| *x != b'\r'); } else { let mut tmpbuf = Vec::new(); vecbuf.iter().enumerate().for_each(|(i, &b)| { @@ -40,7 +37,7 @@ pub fn convert(arguments: &ArgParser, d2u: bool) -> Vec { pub struct Dos2Unix; impl Command for Dos2Unix { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-u") .add_flag("-d") @@ -58,19 +55,23 @@ impl Command for Dos2Unix { } if args.get_flag("--help") { - println!("Usage: dos2unix [-d] [-u] [FILE]"); - print!("\n"); - println!("-d: unix2dos"); - println!("-u: dos2unix (default)"); - exit(0); + let help_text = "\ +Usage: dos2unix [-d] [-u] [FILE] + +-d: unix2dos +-u: dos2unix (default)\ +"; + bail!("{}", help_text) } let result = convert(&args, dos2unix); if args.get_normal_args().len() < 1 { - let _ = io::stdout().write_all(&result); + io::stdout().write_all(&result)?; } else { - let _ = std::fs::write(args.get_normal_args()[0].clone(), &result); + std::fs::write(args.get_normal_args()[0].clone(), &result)?; } + + Ok(()) } } diff --git a/coreutils/src/commands/echo.rs b/coreutils/src/commands/echo.rs index aa96efc..77f92a6 100644 --- a/coreutils/src/commands/echo.rs +++ b/coreutils/src/commands/echo.rs @@ -1,12 +1,15 @@ +use anyhow::Result; use boxutils::commands::Command; pub struct Echo; impl Command for Echo { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args: Vec = std::env::args().collect::>(); let arguments: Vec = boxutils::commands::get_args(String::from("echo"), args); let message = &arguments.join(" "); println!("{}", message); + + Ok(()) } } diff --git a/coreutils/src/commands/env.rs b/coreutils/src/commands/env.rs index 11de7ab..f6672b2 100644 --- a/coreutils/src/commands/env.rs +++ b/coreutils/src/commands/env.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; @@ -35,7 +36,7 @@ impl Env { } impl Command for Env { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-i") .add_flag("-0") @@ -59,20 +60,20 @@ impl Command for Env { if to_run.is_empty() { Env::print_all_vars(); - return; + return Ok(()); } if let Some((command, args)) = to_run.split_first() { std::process::Command::new(command) .args(args) - .spawn() - .expect("env: failed to start command") - .wait() - .expect("env: could not wait for command"); + .spawn()? + .wait()?; } if null_terminate { print!("\0"); } + + Ok(()) } } diff --git a/coreutils/src/commands/false.rs b/coreutils/src/commands/false.rs index d162e34..a66a0ee 100644 --- a/coreutils/src/commands/false.rs +++ b/coreutils/src/commands/false.rs @@ -1,10 +1,11 @@ +use anyhow::Result; use boxutils::commands::Command; use std::process::exit; pub struct False; impl Command for False { - fn execute(&self) { + fn execute(&self) -> Result<()> { exit(1); } } diff --git a/coreutils/src/commands/hello.rs b/coreutils/src/commands/hello.rs index e6763a5..d483736 100644 --- a/coreutils/src/commands/hello.rs +++ b/coreutils/src/commands/hello.rs @@ -1,9 +1,12 @@ +use anyhow::Result; use boxutils::commands::Command; pub struct Hello; impl Command for Hello { - fn execute(&self) { + fn execute(&self) -> Result<()> { println!("Hello, world!"); + + Ok(()) } } diff --git a/coreutils/src/commands/hostname.rs b/coreutils/src/commands/hostname.rs index da0826d..b4a15f3 100644 --- a/coreutils/src/commands/hostname.rs +++ b/coreutils/src/commands/hostname.rs @@ -1,10 +1,11 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Hostname; impl Command for Hostname { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .parse_args("hostname"); @@ -19,5 +20,7 @@ impl Command for Hostname { }; println!("{}", hostname.to_string_lossy()); + + Ok(()) } } diff --git a/coreutils/src/commands/ln.rs b/coreutils/src/commands/ln.rs index b851c3e..fbd123f 100644 --- a/coreutils/src/commands/ln.rs +++ b/coreutils/src/commands/ln.rs @@ -1,11 +1,11 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs; - pub struct Ln; impl Command for Ln { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .add_flag("-s") @@ -20,7 +20,7 @@ impl Command for Ln { let help = args.get_normal_args().len() != 2 || args.get_flag("--help"); if help { println!("Usage: ln [-sfnbtv] [-S SUF] TARGET LINK"); - return; + return Ok(()); } let to_be_linked = args.get_normal_args()[0].clone(); @@ -31,11 +31,11 @@ impl Command for Ln { } if args.get_flag("-f") { - if fs::exists(&destination).unwrap() { - if fs::metadata(&destination).unwrap().is_dir() { - fs::remove_dir_all(&destination).unwrap(); + if fs::exists(&destination)? { + if fs::metadata(&destination)?.is_dir() { + fs::remove_dir_all(&destination)?; } else { - fs::remove_file(&destination).unwrap(); + fs::remove_file(&destination)?; } } } @@ -47,19 +47,22 @@ impl Command for Ln { if args.get_flag("-b") { let suffix = args.get_option("-S").unwrap_or("~"); let new_filename = format!("{}{}", destination, suffix); - let _ = fs::rename(&destination, &new_filename); + fs::rename(&destination, &new_filename)?; } if args.get_flag("-s") { - let symlink_result = boxutils::cross::fs::symlink(to_be_linked, destination, dereference); + let symlink_result = + boxutils::cross::fs::symlink(to_be_linked, destination, dereference); if let Err(e) = symlink_result { - eprintln!("ln: failed to create symlink: {}", e); + bail!("ln: failed to create symlink: {}", e); } } else { if let Err(e) = boxutils::cross::fs::hard_link(to_be_linked, destination, dereference) { - eprintln!("ln: failed to create hard link: {}", e); + bail!("ln: failed to create hard link: {}", e); } } + + Ok(()) } } diff --git a/coreutils/src/commands/mkdir.rs b/coreutils/src/commands/mkdir.rs index f2f2371..8e24f65 100644 --- a/coreutils/src/commands/mkdir.rs +++ b/coreutils/src/commands/mkdir.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs; @@ -5,7 +6,7 @@ use std::fs; pub struct Mkdir; impl Command for Mkdir { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-p") .add_flag("--parents") @@ -14,11 +15,11 @@ impl Command for Mkdir { if args.get_flag("--help") { println!("Usage: mkdir [DIR1] [DIR2] etc. pp. [-p, --parents]"); - return; + return Ok(()); } if args.get_normal_args().len() == 0 { - panic!("Usage: mkdir [DIR1] [DIR1] etc. pp. [-p, --parents]"); + bail!("Usage: mkdir [DIR1] [DIR1] etc. pp. [-p, --parents]"); } let parented = args.get_flag("-p") || args.get_flag("--parents"); @@ -27,10 +28,12 @@ impl Command for Mkdir { for dir in to_create { if parented { - let _ = fs::create_dir_all(dir); + fs::create_dir_all(dir)?; } else { - let _ = fs::create_dir(dir); + fs::create_dir(dir)?; } } + + Ok(()) } } diff --git a/coreutils/src/commands/nproc.rs b/coreutils/src/commands/nproc.rs index f0bedd3..3dfaa44 100644 --- a/coreutils/src/commands/nproc.rs +++ b/coreutils/src/commands/nproc.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; use num_cpus::get; @@ -5,7 +6,7 @@ use num_cpus::get; pub struct Nproc; impl Command for Nproc { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .add_flag("--ignore") @@ -23,7 +24,7 @@ Prints the number of available CPUs to stdout. --ignore=N, --ignore N Ignore N CPUs " ); - return; + return Ok(()); } if args.get_flag("--all") { @@ -31,13 +32,13 @@ Prints the number of available CPUs to stdout. } if args.get_flag("--ignore") { - ignore = args.get_option("--ignore").unwrap().parse().unwrap(); + ignore = args.get_option("--ignore").unwrap().parse()?; } for argument in args.get_normal_args() { if let Some((k, v)) = argument.split_once('=') { if k == "--ignore" { - ignore = v.parse().unwrap(); + ignore = v.parse()?; } } } @@ -47,5 +48,7 @@ Prints the number of available CPUs to stdout. } else { println!("{}", get() as u64 - ignore) } + + Ok(()) } } diff --git a/coreutils/src/commands/pwd.rs b/coreutils/src/commands/pwd.rs index c2f54ed..f879b3a 100644 --- a/coreutils/src/commands/pwd.rs +++ b/coreutils/src/commands/pwd.rs @@ -1,18 +1,21 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Pwd; impl Command for Pwd { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().add_flag("--help").parse_args("yes"); if args.get_flag("--help") { println!("Usage: pwd"); - return; + return Ok(()); } - let pwd = std::env::current_dir().unwrap(); + let pwd = std::env::current_dir()?; println!("{}", pwd.display()); + + Ok(()) } } diff --git a/coreutils/src/commands/seq.rs b/coreutils/src/commands/seq.rs index 13c6694..075dc81 100644 --- a/coreutils/src/commands/seq.rs +++ b/coreutils/src/commands/seq.rs @@ -1,9 +1,10 @@ +use anyhow::{Result, bail}; use boxutils::{args::ArgParser, commands::Command}; pub struct Seq; impl Command for Seq { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_option("-s") .add_flag("-w") @@ -31,7 +32,7 @@ impl Command for Seq { incnum = inc.parse().expect("seq: invalid inc number"); lastnum = last.parse().expect("seq: invalid last number"); } - _ => panic!("seq: malformed arguments"), + _ => bail!("seq: malformed arguments"), } let mut accumulator = firstnum; @@ -62,5 +63,7 @@ impl Command for Seq { print!("{}", separator); } } + + Ok(()) } } diff --git a/coreutils/src/commands/sleep.rs b/coreutils/src/commands/sleep.rs index 413c545..4f5fe60 100644 --- a/coreutils/src/commands/sleep.rs +++ b/coreutils/src/commands/sleep.rs @@ -1,15 +1,16 @@ +use anyhow::{Result, bail}; use boxutils::{args::ArgParser, commands::Command}; pub struct Sleep; impl Command for Sleep { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().parse_args("sleep"); let mut sleep_for = 0; for arg in args.get_normal_args() { if arg.chars().last().unwrap().is_numeric() { - sleep_for += arg.parse::().unwrap(); + sleep_for += arg.parse::()?; } else { let multiplier = match arg.chars().last().unwrap() { 's' => 1, @@ -17,20 +18,21 @@ impl Command for Sleep { 'h' => 3600, 'd' => 86400, _ => { - println!("Invalid time interval '{}'", arg); - return; + bail!("Invalid time interval '{}'", arg); } }; - sleep_for += arg[..arg.len() - 1].parse::().unwrap() * multiplier; + sleep_for += arg[..arg.len() - 1].parse::()? * multiplier; } } if sleep_for == 0 { println!("Usage: sleep [N]..."); - return; + return Ok(()); } std::thread::sleep(std::time::Duration::from_secs(sleep_for as u64)); + + Ok(()) } } diff --git a/coreutils/src/commands/tee.rs b/coreutils/src/commands/tee.rs index 46bbe71..c8b8672 100644 --- a/coreutils/src/commands/tee.rs +++ b/coreutils/src/commands/tee.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::fs::OpenOptions; @@ -10,7 +11,7 @@ use std::io::Write; pub struct Tee; impl Command for Tee { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("--help") .add_flag("-a") @@ -34,17 +35,19 @@ impl Command for Tee { if let Ok(this_file) = this_file { writes.push(Box::new(this_file)); } else { - eprintln!("tee: unable to open file: {}", file); + bail!("tee: unable to open file: {}", file); } } let mut buffer = String::new(); while boxutils::input::repl(&mut buffer) { for output in &mut writes { - let _ = output.write_all(buffer.as_bytes()); - let _ = output.flush(); + output.write_all(buffer.as_bytes())?; + output.flush()?; } buffer.clear(); } + + Ok(()) } } diff --git a/coreutils/src/commands/test.rs b/coreutils/src/commands/test.rs index 2de7ea6..2959acb 100644 --- a/coreutils/src/commands/test.rs +++ b/coreutils/src/commands/test.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; @@ -194,7 +195,7 @@ impl Test { } impl Command for Test { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flags(STRING_TESTS.into()) .add_flags(NUMBER_TESTS.into()) @@ -202,7 +203,7 @@ impl Command for Test { .parse_args(if self.bracket { "[" } else { "test" }); if self.bracket && !args.get_flag("]") { - panic!("[: missing ]"); + bail!("[: missing ]"); } if STRING_TESTS.iter().any(|&x| args.get_flag(x)) { @@ -214,5 +215,6 @@ impl Command for Test { } exit_false(); + unreachable!(); } } diff --git a/coreutils/src/commands/true.rs b/coreutils/src/commands/true.rs index 3dcaf9c..2ceb189 100644 --- a/coreutils/src/commands/true.rs +++ b/coreutils/src/commands/true.rs @@ -1,10 +1,11 @@ +use anyhow::Result; use boxutils::commands::Command; use std::process::exit; pub struct True; impl Command for True { - fn execute(&self) { + fn execute(&self) -> Result<()> { exit(0); } } diff --git a/coreutils/src/commands/unix2dos.rs b/coreutils/src/commands/unix2dos.rs index cccbef6..98d212e 100644 --- a/coreutils/src/commands/unix2dos.rs +++ b/coreutils/src/commands/unix2dos.rs @@ -1,4 +1,5 @@ use crate::commands::dos2unix::convert; +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; use std::io::{self, Write}; @@ -7,7 +8,7 @@ use std::process::exit; pub struct Unix2Dos; impl Command for Unix2Dos { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder() .add_flag("-u") .add_flag("-d") @@ -35,10 +36,11 @@ impl Command for Unix2Dos { let result = convert(&args, dos2unix); if args.get_normal_args().len() < 1 { - let _ = io::stdout().write_all(&result); + io::stdout().write_all(&result)?; } else { - let _ = std::fs::write(args.get_normal_args()[0].clone(), &result); + std::fs::write(args.get_normal_args()[0].clone(), &result)?; } + Ok(()) } } diff --git a/coreutils/src/commands/whoami.rs b/coreutils/src/commands/whoami.rs index 0f501da..ee322bc 100644 --- a/coreutils/src/commands/whoami.rs +++ b/coreutils/src/commands/whoami.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; use boxutils::cross::user; @@ -5,16 +6,18 @@ use boxutils::cross::user; pub struct WhoAmI; impl Command for WhoAmI { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().add_flag("--help").parse_args("whoami"); if args.get_flag("--help") { println!("Usage: whoami"); - return; + return Ok(()); } - let username = user::get_username().unwrap_or_else(|| "unknown".to_string()); + let username = user::get_username().unwrap_or("unknown".to_string()); println!("{}", username); + + Ok(()) } } diff --git a/coreutils/src/commands/yes.rs b/coreutils/src/commands/yes.rs index 14df289..edad407 100644 --- a/coreutils/src/commands/yes.rs +++ b/coreutils/src/commands/yes.rs @@ -1,15 +1,16 @@ +use anyhow::Result; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Yes; impl Command for Yes { - fn execute(&self) { + fn execute(&self) -> Result<()> { let args = ArgParser::builder().add_flag("--help").parse_args("yes"); if args.get_flag("--help") { println!("Usage: yes [STRING]"); - return; + return Ok(()); } let string = if args.get_normal_args().is_empty() { diff --git a/shell/Cargo.toml b/shell/Cargo.toml index 2bdf0a5..799dc24 100644 --- a/shell/Cargo.toml +++ b/shell/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] boxutils.workspace = true +anyhow.workspace = true diff --git a/shell/src/ash.rs b/shell/src/ash.rs index 4153e2f..c874177 100644 --- a/shell/src/ash.rs +++ b/shell/src/ash.rs @@ -1,14 +1,17 @@ use boxutils::commands::Command; + use std::env; use std::io::{self, Write}; use std::process; +use anyhow::{Error, Result}; + use crate::built_in::{Action, run_if_exists}; pub struct Ash; impl Command for Ash { - fn execute(&self) { + fn execute(&self) -> Result<(), Error> { let mut path = env::current_dir().unwrap().display().to_string(); loop { @@ -20,13 +23,13 @@ impl Command for Ash { print!("{} {} ", path.clone(), userchar); let _ = io::stdout().flush(); let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); + io::stdin().read_line(&mut input)?; let mut full_cmd = input.trim().split_whitespace(); - let command = full_cmd.next().unwrap_or(" "); + let command = full_cmd.next().unwrap_or(""); let arguments: Vec<&str> = full_cmd.collect(); if let Some(action) = run_if_exists(command.to_string(), arguments.clone()) { - match action { + match action? { Action::Exit => { break; } @@ -39,17 +42,13 @@ impl Command for Ash { Action::Nothing => {} } } else { - let out = process::Command::new(command) + process::Command::new(command) .args(arguments.clone()) - .spawn(); - - match out { - Ok(mut out) => { - let _ = out.wait(); - } - Err(err) => println!("{:?}", err), - } + .spawn()? + .wait()?; } } + + Ok(()) } } diff --git a/shell/src/built_in/cd.rs b/shell/src/built_in/cd.rs index c20adfc..e4a8df5 100644 --- a/shell/src/built_in/cd.rs +++ b/shell/src/built_in/cd.rs @@ -1,9 +1,10 @@ use crate::built_in::Action; +use anyhow::{Result, bail}; -pub fn cd(arguments: Vec<&str>) -> Action { +pub fn cd(arguments: Vec<&str>) -> Result { if arguments.len() < 1 { - panic!("cd expects **one** argument"); + bail!("cd expects **one** argument"); } - Action::ChangeDirectory(arguments[0].to_owned().clone()) + Ok(Action::ChangeDirectory(arguments[0].to_owned().clone())) } diff --git a/shell/src/built_in/eval.rs b/shell/src/built_in/eval.rs index 203b0cc..efa600d 100644 --- a/shell/src/built_in/eval.rs +++ b/shell/src/built_in/eval.rs @@ -1,17 +1,16 @@ use crate::built_in::Action; +use anyhow::{Result, bail}; use std::process::Command; -pub fn eval(arguments: Vec<&str>) -> Action { +pub fn eval(arguments: Vec<&str>) -> Result { if arguments.len() < 1 { - panic!("eval expects **one or more** arguments"); + bail!("eval expects **one or more** arguments"); } - let output = Command::new(arguments[0]).args(&arguments[1..]).spawn(); - match output { - Ok(mut output) => { - output.wait().expect("failed to wait for process exit"); - } - Err(err) => println!("{:?}", err) - } - Action::Nothing -} \ No newline at end of file + Command::new(arguments[0]) + .args(&arguments[1..]) + .spawn()? + .wait()?; + Ok(Action::Nothing) +} + diff --git a/shell/src/built_in/exit.rs b/shell/src/built_in/exit.rs index c0e6922..f6298b4 100644 --- a/shell/src/built_in/exit.rs +++ b/shell/src/built_in/exit.rs @@ -1,5 +1,6 @@ use crate::built_in::Action; +use anyhow::Result; -pub fn exit(_: Vec<&str>) -> Action { - Action::Exit +pub fn exit(_: Vec<&str>) -> Result { + Ok(Action::Exit) } diff --git a/shell/src/built_in/mod.rs b/shell/src/built_in/mod.rs index 47e5c73..1906a68 100644 --- a/shell/src/built_in/mod.rs +++ b/shell/src/built_in/mod.rs @@ -1,21 +1,23 @@ mod cd; -mod exit; mod eval; +mod exit; + +use anyhow::Result; #[derive(Debug)] pub enum Action { Exit, ChangeDirectory(String), - Nothing + Nothing, } -fn get_function(command: String) -> Option) -> Action> { +fn get_function(command: String) -> Option) -> Result> { let registry = [ - ("exit", exit::exit as fn(Vec<&str>) -> Action), - ("cd", cd::cd as fn(Vec<&str>) -> Action), - ("eval", eval::eval as fn(Vec<&str>) -> Action) + ("exit", exit::exit as fn(Vec<&str>) -> Result), + ("cd", cd::cd as fn(Vec<&str>) -> Result), + ("eval", eval::eval as fn(Vec<&str>) -> Result), ]; - let mut function: Option) -> Action> = None; + let mut function: Option) -> Result> = None; for entry in registry { if entry.0 == &command { @@ -26,7 +28,7 @@ fn get_function(command: String) -> Option) -> Action> { function } -pub fn run_if_exists(command: String, arguments: Vec<&str>) -> Option { +pub fn run_if_exists(command: String, arguments: Vec<&str>) -> Option> { let function = get_function(command); if let Some(function) = function { let action = function(arguments); diff --git a/src/boxcmd.rs b/src/boxcmd.rs index 6ddbb6a..b2334d6 100644 --- a/src/boxcmd.rs +++ b/src/boxcmd.rs @@ -1,11 +1,12 @@ use super::registry::get_registry; +use anyhow::{Result, bail}; use boxutils::args::ArgParser; use boxutils::commands::Command; pub struct Boxcmd; impl Command for Boxcmd { - fn execute(&self) { + fn execute(&self) -> Result<()> { let parser = ArgParser::builder().parse_args("box"); let registry = get_registry(); @@ -15,11 +16,10 @@ impl Command for Boxcmd { continue; } - registry.execute(&command); - return; + registry.execute(&command)?; } - println!( + bail!( "No valid command provided. Included commands:\n{}", registry.list().join(", ") ); diff --git a/src/main.rs b/src/main.rs index 343fc74..3a0959b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ mod boxcmd; mod registry; +use anyhow::{Error, Result}; use boxutils::commands::being_called_as; -fn main() { +fn main() -> Result<(), Error> { let utility_name = being_called_as(); - registry::get_registry().execute(&utility_name); + registry::get_registry().execute(&utility_name)?; + + Ok(()) } diff --git a/utils/Cargo.toml b/utils/Cargo.toml index c6d1cb8..63a4b2a 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow.workspace = true diff --git a/utils/src/commands.rs b/utils/src/commands.rs index 5d3ad17..837c802 100644 --- a/utils/src/commands.rs +++ b/utils/src/commands.rs @@ -2,6 +2,8 @@ use std::env; use std::mem; use std::path::Path; +use anyhow::{Error, Result}; + pub fn being_called_as() -> String { let args = env::args().collect::>(); let exe_path = args[0].clone(); @@ -34,5 +36,5 @@ pub fn get_args(commandname: String, args: Vec) -> Vec { } pub trait Command { - fn execute(&self); + fn execute(&self) -> Result<(), Error>; } diff --git a/utils/src/registry.rs b/utils/src/registry.rs index 4ee4e0d..61a3480 100644 --- a/utils/src/registry.rs +++ b/utils/src/registry.rs @@ -1,6 +1,8 @@ use super::commands::Command; use std::collections::HashMap; +use anyhow::{Error, Result, bail}; + pub struct CommandRegistry { commands: HashMap>, } @@ -28,23 +30,31 @@ impl CommandRegistry { self.commands.get(name) } - pub fn execute(&self, name: &str) { + pub fn execute(&self, name: &str) -> Result<(), Error> { if let Some(command) = self.get(name) { - command.execute(); + command.execute()?; } else { - println!("Command not found: {}", name); + bail!("Command not found: {}", name); } + + Ok(()) } } mod tests { + #[allow(unused_imports)] // why the heck is rust saying it's unused?? + use anyhow::{Error, Result}; + #[test] fn test_register() { use super::Command; use super::CommandRegistry; + struct TestCommand; impl Command for TestCommand { - fn execute(&self) {} + fn execute(&self) -> Result<(), Error> { + Ok(()) + } } let mut registry = CommandRegistry::new(); @@ -53,18 +63,23 @@ mod tests { } #[test] - fn test_execute() { + fn test_execute() -> Result<(), Error> { use super::Command; use super::CommandRegistry; + struct TestCommand; impl Command for TestCommand { - fn execute(&self) { + fn execute(&self) -> Result<(), Error> { println!("TestCommand executed"); + + Ok(()) } } let mut registry = CommandRegistry::new(); registry.register("test", Box::new(TestCommand)); - registry.execute("test"); + registry.execute("test")?; + + Ok(()) } } From 41fb50c4cfe1f52248416c40dabac1f7d17a7e9c Mon Sep 17 00:00:00 2001 From: teesh3rt Date: Fri, 6 Feb 2026 15:40:43 +0200 Subject: [PATCH 3/3] feat: make a simple nix devshell, integrated with direnv --- .envrc | 1 + .gitignore | 1 + flake.lock | 77 ++++++++++++++++++++++++++++++++++++++++ flake.nix | 13 +++++++ packages/nix/shell.nix | 7 ++++ packages/nix/systems.nix | 5 +++ 6 files changed, 104 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 packages/nix/shell.nix create mode 100644 packages/nix/systems.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index eb5a316..75e92a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +.direnv diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1d3cd3b --- /dev/null +++ b/flake.lock @@ -0,0 +1,77 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "import-tree": { + "locked": { + "lastModified": 1763762820, + "narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=", + "owner": "vic", + "repo": "import-tree", + "rev": "3c23749d8013ec6daa1d7255057590e9ca726646", + "type": "github" + }, + "original": { + "owner": "vic", + "repo": "import-tree", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1770197578, + "narHash": "sha256-AYqlWrX09+HvGs8zM6ebZ1pwUqjkfpnv8mewYwAo+iM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "00c21e4c93d963c50d4c0c89bfa84ed6e0694df2", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1769909678, + "narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "72716169fe93074c333e8d0173151350670b824c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "import-tree": "import-tree", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..f41336c --- /dev/null +++ b/flake.nix @@ -0,0 +1,13 @@ +{ + description = "rebox: remaking busybox with rust"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + import-tree.url = "github:vic/import-tree"; + }; + + outputs = inputs: + inputs.flake-parts.lib.mkFlake {inherit inputs;} + (inputs.import-tree ./packages/nix); +} diff --git a/packages/nix/shell.nix b/packages/nix/shell.nix new file mode 100644 index 0000000..e27e1be --- /dev/null +++ b/packages/nix/shell.nix @@ -0,0 +1,7 @@ +{...}: { + perSystem = {pkgs, ...}: { + devShells.default = pkgs.mkShell { + packages = [pkgs.cargo]; + }; + }; +} diff --git a/packages/nix/systems.nix b/packages/nix/systems.nix new file mode 100644 index 0000000..98d0555 --- /dev/null +++ b/packages/nix/systems.nix @@ -0,0 +1,5 @@ +{...}: { + systems = [ + "x86_64-linux" + ]; +}