rustutils_core/
lib.rs

1//! Multi-call binary for modern coreutils implemented in Rust, with a focus on
2//! simplicity.
3#![deny(warnings)]
4#![deny(unsafe_code)]
5#![deny(missing_docs)]
6
7use clap::Parser;
8use rustutils_runnable::Runnable;
9use std::error::Error;
10use std::process::ExitCode;
11mod macros;
12
13// Generate the `Coreutil` enum, which has cases for every utility compiled in,
14// as well as some helper methods used for pasing arguments when run as a multi-call
15// binary.
16macros::declare_utility! {
17    "arch" => rustutils_arch::Arch as Arch,
18    "cat" => rustutils_cat::Cat as Cat,
19    "false" => rustutils_false::False as False,
20    "pwd" => rustutils_pwd::Pwd as Pwd,
21    "rmdir" => rustutils_rmdir::Rmdir as Rmdir,
22    "seq" => rustutils_seq::Seq as Seq,
23    "sleep" => rustutils_sleep::Sleep as Sleep,
24    "tee" => rustutils_tee::Tee as Tee,
25    "true" => rustutils_true::True as True,
26    "uname" => rustutils_uname::Uname as Uname,
27    "unlink" => rustutils_unlink::Unlink as Unlink,
28    "wc" => rustutils_wc::Wc as Wc,
29    "yes" => rustutils_yes::Yes as Yes,
30    "dirname" => rustutils_dirname::Dirname as Dirname,
31    "basename" => rustutils_basename::Basename as Basename,
32    "mkdir" => rustutils_mkdir::Mkdir as Mkdir,
33    "env" => rustutils_env::Env as Env,
34    "printenv" => rustutils_printenv::Printenv as Printenv,
35    "base64" => rustutils_base64::Base64 as Base64,
36    "factor" => rustutils_factor::Factor as Factor
37}
38
39/// Command-line options for the non-multicall invocation of this binary, where every
40/// utility is available as a subcommand. This is used as the fallback.
41#[derive(Parser, Clone, Debug)]
42#[clap(author, version, about, long_about = None)]
43pub struct Coreutils {
44    #[clap(subcommand)]
45    util: Coreutil,
46}
47
48impl Runnable for Coreutils {
49    fn run(&self) -> Result<(), Box<dyn Error>> {
50        self.util.runnable().run()
51    }
52
53    fn main(&self) -> ExitCode {
54        self.util.runnable().main()
55    }
56}
57
58/// Check if this binary is being called as another utility, and if so, return
59/// the parsed command-line options of that utility.
60pub fn multicall_parse() -> Result<Option<Coreutil>, Box<dyn Error>> {
61    let executable = std::env::current_exe()?;
62    let file = executable.file_stem().and_then(|s| s.to_str());
63    let result = file.and_then(|name| Coreutil::parse(name));
64    Ok(result)
65}
66
67/// Run the coreutils implementation.
68///
69/// When the `multicall` feature is enabled, this will attempt to look at the name of
70/// the executable file that this was launched as, and run the corresponding utility.
71pub fn main() -> ExitCode {
72    if cfg!(feature = "multicall") {
73        match multicall_parse() {
74            Ok(Some(utility)) => utility.runnable().main(),
75            Ok(None) => Coreutils::parse().main(),
76            Err(error) => {
77                eprintln!("Error parsing multicall: {error}");
78                ExitCode::FAILURE
79            }
80        }
81    } else {
82        Coreutils::parse().main()
83    }
84}