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 touch;
26mod r#true;
27mod which;
28mod yes;
29
30pub use basename::basename;
31pub use cat::cat;
32pub use cd::cd;
33pub use cp::cp;
34pub use dirname::dirname;
35pub use echo::echo;
36pub use env::env;
37pub use exit::exit;
38pub use ls::ls;
39pub use mkdir::mkdir;
40pub use mv::mv;
41pub use pwd::pwd;
42pub use r#false::r#false;
43pub use r#true::r#true;
44pub use rm::rm;
45pub use seq::seq;
46pub use sleep::sleep;
47pub use tee::tee;
48pub use test::test;
49pub use touch::touch;
50pub use which::which;
51pub use yes::yes;
52
53use crate::utils::CommandResult;
54use std::collections::HashMap;
55use tokio::sync::mpsc;
56
57pub struct CommandContext {
59 pub args: Vec<String>,
61 pub stdin: Option<String>,
63 pub cwd: Option<std::path::PathBuf>,
65 pub env: Option<HashMap<String, String>>,
67 pub output_tx: Option<mpsc::Sender<StreamChunk>>,
69 pub is_cancelled: Option<Box<dyn Fn() -> bool + Send + Sync>>,
71}
72
73impl std::fmt::Debug for CommandContext {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("CommandContext")
76 .field("args", &self.args)
77 .field("stdin", &self.stdin)
78 .field("cwd", &self.cwd)
79 .field("env", &self.env)
80 .field("output_tx", &self.output_tx.is_some())
81 .field("is_cancelled", &self.is_cancelled.is_some())
82 .finish()
83 }
84}
85
86#[derive(Debug, Clone)]
88pub enum StreamChunk {
89 Stdout(String),
90 Stderr(String),
91}
92
93impl CommandContext {
94 pub fn new(args: Vec<String>) -> Self {
96 CommandContext {
97 args,
98 stdin: None,
99 cwd: None,
100 env: None,
101 output_tx: None,
102 is_cancelled: None,
103 }
104 }
105
106 pub fn is_cancelled(&self) -> bool {
108 self.is_cancelled.as_ref().map(|f| f()).unwrap_or(false)
109 }
110
111 pub fn get_cwd(&self) -> std::path::PathBuf {
113 self.cwd.clone().unwrap_or_else(|| {
114 std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("/"))
115 })
116 }
117}
118
119pub type VirtualCommandHandler =
121 fn(
122 CommandContext,
123 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = CommandResult> + Send>>;
124
125pub struct VirtualCommandRegistry {
127 commands: HashMap<String, VirtualCommandHandler>,
128}
129
130impl Default for VirtualCommandRegistry {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl VirtualCommandRegistry {
137 pub fn new() -> Self {
139 VirtualCommandRegistry {
140 commands: HashMap::new(),
141 }
142 }
143
144 pub fn with_builtins() -> Self {
146 let mut registry = Self::new();
147 registry.register_builtins();
148 registry
149 }
150
151 pub fn register(&mut self, name: &str, handler: VirtualCommandHandler) {
153 self.commands.insert(name.to_string(), handler);
154 }
155
156 pub fn unregister(&mut self, name: &str) -> bool {
158 self.commands.remove(name).is_some()
159 }
160
161 pub fn get(&self, name: &str) -> Option<&VirtualCommandHandler> {
163 self.commands.get(name)
164 }
165
166 pub fn contains(&self, name: &str) -> bool {
168 self.commands.contains_key(name)
169 }
170
171 pub fn list(&self) -> Vec<&str> {
173 self.commands.keys().map(|s| s.as_str()).collect()
174 }
175
176 pub fn register_builtins(&mut self) {
178 }
182}
183
184static VIRTUAL_COMMANDS_ENABLED: std::sync::atomic::AtomicBool =
186 std::sync::atomic::AtomicBool::new(true);
187
188pub fn enable_virtual_commands() {
190 VIRTUAL_COMMANDS_ENABLED.store(true, std::sync::atomic::Ordering::SeqCst);
191}
192
193pub fn disable_virtual_commands() {
195 VIRTUAL_COMMANDS_ENABLED.store(false, std::sync::atomic::Ordering::SeqCst);
196}
197
198pub fn are_virtual_commands_enabled() -> bool {
200 VIRTUAL_COMMANDS_ENABLED.load(std::sync::atomic::Ordering::SeqCst)
201}