Skip to main content

wasibox_core/
lib.rs

1#[cfg(test)]
2mod tests;
3pub mod utils;
4
5use std::ffi::OsString;
6use std::io::{Read, Write};
7use std::path::Path;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11/// A token to cancel running commands.
12#[derive(Clone, Default)]
13pub struct CancellationToken {
14    flag: Arc<AtomicBool>,
15}
16
17impl CancellationToken {
18    pub fn new() -> Self {
19        Self {
20            flag: Arc::new(AtomicBool::new(false)),
21        }
22    }
23
24    pub fn cancel(&self) {
25        self.flag.store(true, Ordering::SeqCst);
26    }
27
28    pub fn is_cancelled(&self) -> bool {
29        self.flag.load(Ordering::SeqCst)
30    }
31
32    pub fn reset(&self) {
33        self.flag.store(false, Ordering::SeqCst);
34    }
35}
36
37/// Context for utility IO, allowing redirection of stdin, stdout, and stderr.
38pub struct IoContext {
39    pub stdin: Box<dyn Read + Send>,
40    pub stdout: Box<dyn Write + Send>,
41    pub stderr: Box<dyn Write + Send>,
42    pub cancel_token: CancellationToken,
43}
44
45impl IoContext {
46    pub fn new(
47        stdin: Box<dyn Read + Send>,
48        stdout: Box<dyn Write + Send>,
49        stderr: Box<dyn Write + Send>,
50    ) -> Self {
51        Self {
52            stdin,
53            stdout,
54            stderr,
55            cancel_token: CancellationToken::new(),
56        }
57    }
58
59    pub fn with_cancel(
60        stdin: Box<dyn Read + Send>,
61        stdout: Box<dyn Write + Send>,
62        stderr: Box<dyn Write + Send>,
63        cancel_token: CancellationToken,
64    ) -> Self {
65        Self {
66            stdin,
67            stdout,
68            stderr,
69            cancel_token,
70        }
71    }
72}
73
74impl Default for IoContext {
75    fn default() -> Self {
76        Self {
77            stdin: Box::new(std::io::stdin()),
78            stdout: Box::new(std::io::stdout()),
79            stderr: Box::new(std::io::stderr()),
80            cancel_token: CancellationToken::new(),
81        }
82    }
83}
84
85pub fn execute<I, T>(args: I) -> Result<(), String>
86where
87    I: IntoIterator<Item = T>,
88    T: Into<OsString> + Clone,
89{
90    execute_with_context(args, &mut IoContext::default())
91}
92
93pub fn execute_with_context<I, T>(args: I, ctx: &mut IoContext) -> Result<(), String>
94where
95    I: IntoIterator<Item = T>,
96    T: Into<OsString> + Clone,
97{
98    let args_vec: Vec<OsString> = args.into_iter().map(|a| a.into()).collect();
99    if args_vec.is_empty() {
100        return Err("No utility specified".to_string());
101    }
102
103    let prog_name_raw = Path::new(&args_vec[0])
104        .file_name()
105        .unwrap_or_default()
106        .to_string_lossy();
107    let prog_name = prog_name_raw
108        .trim_end_matches(".exe")
109        .trim_end_matches(".wasm");
110
111    let (util_name, util_args) =
112        if prog_name == "core" || prog_name == "core-rust" || prog_name == "wasibox-core" {
113            if args_vec.len() < 2 {
114                return Err("Usage: core <utility> [args...]".to_string());
115            }
116            (args_vec[1].to_string_lossy().to_string(), &args_vec[1..])
117        } else {
118            (prog_name.to_string(), &args_vec[..])
119        };
120
121    match util_name.as_str() {
122        #[cfg(feature = "arch")]
123        "arch" => utils::arch::execute_with_context(util_args, ctx),
124        #[cfg(feature = "basename")]
125        "basename" => utils::basename::execute_with_context(util_args, ctx),
126        #[cfg(feature = "cat")]
127        "cat" => utils::cat::execute_with_context(util_args, ctx),
128        #[cfg(feature = "cp")]
129        "cp" => utils::cp::execute_with_context(util_args, ctx),
130        #[cfg(feature = "dir")]
131        "dir" => utils::dir::execute_with_context(util_args, ctx),
132        #[cfg(feature = "dirname")]
133        "dirname" => utils::dirname::execute_with_context(util_args, ctx),
134        #[cfg(feature = "echo")]
135        "echo" => utils::echo::execute_with_context(util_args, ctx),
136        #[cfg(feature = "env")]
137        "env" => utils::env::execute_with_context(util_args, ctx),
138        #[cfg(feature = "false")]
139        "false" => utils::r#false::execute_with_context(util_args, ctx),
140        #[cfg(feature = "grep")]
141        "grep" => utils::grep::execute_with_context(util_args, ctx),
142        #[cfg(feature = "head")]
143        "head" => utils::head::execute_with_context(util_args, ctx),
144        #[cfg(feature = "link")]
145        "link" => utils::link::execute_with_context(util_args, ctx),
146        #[cfg(feature = "ln")]
147        "ln" => utils::ln::execute_with_context(util_args, ctx),
148        #[cfg(feature = "ls")]
149        "ls" => utils::ls::execute_with_context(util_args, ctx),
150        #[cfg(feature = "mkdir")]
151        "mkdir" => utils::mkdir::execute_with_context(util_args, ctx),
152        #[cfg(feature = "mv")]
153        "mv" => utils::mv::execute_with_context(util_args, ctx),
154        #[cfg(feature = "pwd")]
155        "pwd" => utils::pwd::execute_with_context(util_args, ctx),
156        #[cfg(feature = "rm")]
157        "rm" => utils::rm::execute_with_context(util_args, ctx),
158        #[cfg(feature = "rmdir")]
159        "rmdir" => utils::rmdir::execute_with_context(util_args, ctx),
160        #[cfg(feature = "seq")]
161        "seq" => utils::seq::execute_with_context(util_args, ctx),
162        #[cfg(feature = "sleep")]
163        "sleep" => utils::sleep::execute_with_context(util_args, ctx),
164        #[cfg(feature = "tail")]
165        "tail" => utils::tail::execute_with_context(util_args, ctx),
166        #[cfg(feature = "tee")]
167        "tee" => utils::tee::execute_with_context(util_args, ctx),
168        #[cfg(feature = "touch")]
169        "touch" => utils::touch::execute_with_context(util_args, ctx),
170        #[cfg(feature = "tree")]
171        "tree" => utils::tree::execute_with_context(util_args, ctx),
172        #[cfg(feature = "true")]
173        "true" => utils::r#true::execute_with_context(util_args, ctx),
174        #[cfg(feature = "uname")]
175        "uname" => utils::uname::execute_with_context(util_args, ctx),
176        #[cfg(feature = "unlink")]
177        "unlink" => utils::unlink::execute_with_context(util_args, ctx),
178        #[cfg(feature = "wc")]
179        "wc" => utils::wc::execute_with_context(util_args, ctx),
180        #[cfg(feature = "whoami")]
181        "whoami" => utils::whoami::execute_with_context(util_args, ctx),
182        #[cfg(feature = "yes")]
183        "yes" => utils::yes::execute_with_context(util_args, ctx),
184        _ => Err(format!("Unknown utility: {}", util_name)),
185    }
186}