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#[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
37pub 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}