command_stream/commands/
mod.rs1mod basename;
8mod cat;
9pub(crate) mod cd;
10mod cp;
11mod dirname;
12mod echo;
13mod env;
14mod exit;
15mod r#false;
16mod ls;
17mod mkdir;
18mod mv;
19mod pwd;
20mod rm;
21mod seq;
22mod sleep;
23mod tee;
24mod test;
25mod text;
26mod touch;
27mod r#true;
28mod which;
29mod yes;
30
31pub use basename::basename;
32pub use cat::cat;
33pub use cd::cd;
34pub use cp::cp;
35pub use dirname::dirname;
36pub use echo::echo;
37pub use env::env;
38pub use exit::exit;
39pub use ls::ls;
40pub use mkdir::mkdir;
41pub use mv::mv;
42pub use pwd::pwd;
43pub use r#false::r#false;
44pub use r#true::r#true;
45pub use rm::rm;
46pub use seq::seq;
47pub use sleep::sleep;
48pub use tee::tee;
49pub use test::test;
50pub use text::{head, sort, tail, uniq};
51pub use touch::touch;
52pub use which::which;
53pub use yes::yes;
54
55use crate::utils::CommandResult;
56use std::collections::HashMap;
57use tokio::sync::mpsc;
58
59pub struct CommandContext {
61 pub args: Vec<String>,
63 pub stdin: Option<String>,
65 pub cwd: Option<std::path::PathBuf>,
67 pub env: Option<HashMap<String, String>>,
69 pub output_tx: Option<mpsc::Sender<StreamChunk>>,
71 pub is_cancelled: Option<Box<dyn Fn() -> bool + Send + Sync>>,
73}
74
75impl std::fmt::Debug for CommandContext {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.debug_struct("CommandContext")
78 .field("args", &self.args)
79 .field("stdin", &self.stdin)
80 .field("cwd", &self.cwd)
81 .field("env", &self.env)
82 .field("output_tx", &self.output_tx.is_some())
83 .field("is_cancelled", &self.is_cancelled.is_some())
84 .finish()
85 }
86}
87
88#[derive(Debug, Clone)]
90pub enum StreamChunk {
91 Stdout(String),
92 Stderr(String),
93}
94
95impl CommandContext {
96 pub fn new(args: Vec<String>) -> Self {
98 CommandContext {
99 args,
100 stdin: None,
101 cwd: None,
102 env: None,
103 output_tx: None,
104 is_cancelled: None,
105 }
106 }
107
108 pub fn is_cancelled(&self) -> bool {
110 self.is_cancelled.as_ref().map(|f| f()).unwrap_or(false)
111 }
112
113 pub fn get_cwd(&self) -> std::path::PathBuf {
115 self.cwd.clone().unwrap_or_else(|| {
116 std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("/"))
117 })
118 }
119}
120
121pub type VirtualCommandHandler =
123 fn(
124 CommandContext,
125 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = CommandResult> + Send>>;
126
127pub struct VirtualCommandRegistry {
129 commands: HashMap<String, VirtualCommandHandler>,
130}
131
132impl Default for VirtualCommandRegistry {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl VirtualCommandRegistry {
139 pub fn new() -> Self {
141 VirtualCommandRegistry {
142 commands: HashMap::new(),
143 }
144 }
145
146 pub fn with_builtins() -> Self {
148 let mut registry = Self::new();
149 registry.register_builtins();
150 registry
151 }
152
153 pub fn register(&mut self, name: &str, handler: VirtualCommandHandler) {
155 self.commands.insert(name.to_string(), handler);
156 }
157
158 pub fn unregister(&mut self, name: &str) -> bool {
160 self.commands.remove(name).is_some()
161 }
162
163 pub fn get(&self, name: &str) -> Option<&VirtualCommandHandler> {
165 self.commands.get(name)
166 }
167
168 pub fn contains(&self, name: &str) -> bool {
170 self.commands.contains_key(name)
171 }
172
173 pub fn list(&self) -> Vec<&str> {
175 self.commands.keys().map(|s| s.as_str()).collect()
176 }
177
178 pub fn register_builtins(&mut self) {
180 macro_rules! register {
181 ($name:literal, $function:path) => {
182 self.register($name, |ctx| Box::pin($function(ctx)));
183 };
184 }
185
186 register!("echo", echo);
187 register!("pwd", pwd);
188 register!("cd", cd);
189 register!("true", r#true);
190 register!("false", r#false);
191 register!("sleep", sleep);
192 register!("cat", cat);
193 register!("ls", ls);
194 register!("mkdir", mkdir);
195 register!("rm", rm);
196 register!("touch", touch);
197 register!("cp", cp);
198 register!("mv", mv);
199 register!("basename", basename);
200 register!("dirname", dirname);
201 register!("env", env);
202 register!("exit", exit);
203 register!("which", which);
204 register!("yes", yes);
205 register!("seq", seq);
206 register!("tee", tee);
207 register!("test", test);
208 register!("head", head);
209 register!("tail", tail);
210 register!("sort", sort);
211 register!("uniq", uniq);
212 }
213}
214
215static VIRTUAL_COMMANDS_ENABLED: std::sync::atomic::AtomicBool =
217 std::sync::atomic::AtomicBool::new(true);
218
219pub fn enable_virtual_commands() {
221 VIRTUAL_COMMANDS_ENABLED.store(true, std::sync::atomic::Ordering::SeqCst);
222}
223
224pub fn disable_virtual_commands() {
226 VIRTUAL_COMMANDS_ENABLED.store(false, std::sync::atomic::Ordering::SeqCst);
227}
228
229pub fn are_virtual_commands_enabled() -> bool {
231 VIRTUAL_COMMANDS_ENABLED.load(std::sync::atomic::Ordering::SeqCst)
232}