From 47f5cfb674f0f822e5bf134266962636960285bc Mon Sep 17 00:00:00 2001 From: teesh3rt Date: Mon, 24 Mar 2025 15:46:05 +0200 Subject: [PATCH] feat: add the test command (incomplete!) --- coreutils/src/commands/mod.rs | 1 + coreutils/src/commands/test.rs | 218 +++++++++++++++++++++++++++++++++ src/registry.rs | 5 + 3 files changed, 224 insertions(+) create mode 100644 coreutils/src/commands/test.rs diff --git a/coreutils/src/commands/mod.rs b/coreutils/src/commands/mod.rs index f8e12de..3464b27 100644 --- a/coreutils/src/commands/mod.rs +++ b/coreutils/src/commands/mod.rs @@ -13,3 +13,4 @@ command!(dd::Dd); command!(nproc::Nproc); command!(r#true::True); command!(r#false::False); +command!(test::Test); diff --git a/coreutils/src/commands/test.rs b/coreutils/src/commands/test.rs new file mode 100644 index 0000000..2de7ea6 --- /dev/null +++ b/coreutils/src/commands/test.rs @@ -0,0 +1,218 @@ +use boxutils::args::ArgParser; +use boxutils::commands::Command; + +// TODO: Add filetype operations +// +// This is not done yet because file operations are +// quite tricky to implement in a way that is both +// platform-independent and safe. + +const STRING_TESTS: &[&str] = &["-z", "-n", "=", "!="]; +const NUMBER_TESTS: &[&str] = &["-eq", "-ne", "-lt", "-le", "-gt", "-ge"]; +const OTHER_FLAGS: &[&str] = &["]"]; + +fn exit_false() { + std::process::exit(1); +} + +fn exit_true() { + std::process::exit(0); +} + +pub struct Test { + bracket: bool, +} + +impl Test { + pub fn with_bracket() -> Test { + Test { bracket: true } + } + + pub fn without_bracket() -> Test { + Test { bracket: false } + } + + fn do_string_ops(args: &ArgParser) { + if args.get_flag("-z") { + let args = args.get_normal_args(); + let string = args.get(0).unwrap_or(&String::from("")).clone(); + if string.is_empty() { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("-n") { + let args = args.get_normal_args(); + let string = args.get(0).unwrap_or(&String::from("")).clone(); + if !string.is_empty() { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("=") { + let args = args.get_normal_args(); + let string1 = args.get(0).unwrap_or(&String::from("")).clone(); + let string2 = args.get(1).unwrap_or(&String::from("")).clone(); + if string1 == string2 { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("!=") { + let args = args.get_normal_args(); + let string1 = args.get(0).unwrap_or(&String::from("")).clone(); + let string2 = args.get(1).unwrap_or(&String::from("")).clone(); + if string1 != string2 { + exit_true(); + } else { + exit_false(); + } + } + } + + fn do_number_ops(args: &ArgParser) { + if args.get_flag("-eq") { + let args = args.get_normal_args(); + let number1 = args + .get(0) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + let number2 = args + .get(1) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + if number1 == number2 { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("-ne") { + let args = args.get_normal_args(); + let number1 = args + .get(0) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + let number2 = args + .get(1) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + if number1 != number2 { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("-lt") { + let args = args.get_normal_args(); + let number1 = args + .get(0) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + let number2 = args + .get(1) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + if number1 < number2 { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("-le") { + let args = args.get_normal_args(); + let number1 = args + .get(0) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + let number2 = args + .get(1) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + if number1 <= number2 { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("-gt") { + let args = args.get_normal_args(); + let number1 = args + .get(0) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + let number2 = args + .get(1) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + if number1 > number2 { + exit_true(); + } else { + exit_false(); + } + } + + if args.get_flag("-ge") { + let args = args.get_normal_args(); + let number1 = args + .get(0) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + let number2 = args + .get(1) + .unwrap_or(&String::from("")) + .parse::() + .unwrap_or(0); + if number1 >= number2 { + exit_true(); + } else { + exit_false(); + } + } + } +} + +impl Command for Test { + fn execute(&self) { + let args = ArgParser::builder() + .add_flags(STRING_TESTS.into()) + .add_flags(NUMBER_TESTS.into()) + .add_flags(OTHER_FLAGS.into()) + .parse_args(if self.bracket { "[" } else { "test" }); + + if self.bracket && !args.get_flag("]") { + panic!("[: missing ]"); + } + + if STRING_TESTS.iter().any(|&x| args.get_flag(x)) { + Test::do_string_ops(&args); + } + + if NUMBER_TESTS.iter().any(|&x| args.get_flag(x)) { + Test::do_number_ops(&args); + } + + exit_false(); + } +} diff --git a/src/registry.rs b/src/registry.rs index 3133880..6a2028a 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -13,6 +13,11 @@ pub fn get_registry() -> CommandRegistry { registry.register("nproc", Box::new(coreutils::commands::Nproc)); registry.register("true", Box::new(coreutils::commands::True)); registry.register("false", Box::new(coreutils::commands::False)); + registry.register( + "test", + Box::new(coreutils::commands::Test::without_bracket()), + ); + registry.register("[", Box::new(coreutils::commands::Test::with_bracket())); registry.register("box", Box::new(Boxcmd)); registry