Skip to main content

command_stream/commands/
mod.rs

1//! Virtual command implementations
2//!
3//! This module contains implementations of shell commands that run in-process
4//! without spawning external processes. These provide faster execution and
5//! consistent behavior across platforms.
6
7mod 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
57/// Context for virtual command execution
58pub struct CommandContext {
59    /// Command arguments (excluding the command name)
60    pub args: Vec<String>,
61    /// Standard input content
62    pub stdin: Option<String>,
63    /// Current working directory
64    pub cwd: Option<std::path::PathBuf>,
65    /// Environment variables
66    pub env: Option<HashMap<String, String>>,
67    /// Channel to send streaming output
68    pub output_tx: Option<mpsc::Sender<StreamChunk>>,
69    /// Cancellation check function
70    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/// A chunk of streaming output
87#[derive(Debug, Clone)]
88pub enum StreamChunk {
89    Stdout(String),
90    Stderr(String),
91}
92
93impl CommandContext {
94    /// Create a new command context with arguments
95    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    /// Check if the command has been cancelled
107    pub fn is_cancelled(&self) -> bool {
108        self.is_cancelled.as_ref().map(|f| f()).unwrap_or(false)
109    }
110
111    /// Get the current working directory
112    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
119/// Type for virtual command handler functions
120pub type VirtualCommandHandler =
121    fn(
122        CommandContext,
123    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = CommandResult> + Send>>;
124
125/// Registry of virtual commands
126pub 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    /// Create a new empty registry
138    pub fn new() -> Self {
139        VirtualCommandRegistry {
140            commands: HashMap::new(),
141        }
142    }
143
144    /// Create a registry with all built-in commands registered
145    pub fn with_builtins() -> Self {
146        let mut registry = Self::new();
147        registry.register_builtins();
148        registry
149    }
150
151    /// Register a virtual command
152    pub fn register(&mut self, name: &str, handler: VirtualCommandHandler) {
153        self.commands.insert(name.to_string(), handler);
154    }
155
156    /// Unregister a virtual command
157    pub fn unregister(&mut self, name: &str) -> bool {
158        self.commands.remove(name).is_some()
159    }
160
161    /// Get a virtual command handler
162    pub fn get(&self, name: &str) -> Option<&VirtualCommandHandler> {
163        self.commands.get(name)
164    }
165
166    /// Check if a command is registered
167    pub fn contains(&self, name: &str) -> bool {
168        self.commands.contains_key(name)
169    }
170
171    /// List all registered command names
172    pub fn list(&self) -> Vec<&str> {
173        self.commands.keys().map(|s| s.as_str()).collect()
174    }
175
176    /// Register all built-in commands
177    pub fn register_builtins(&mut self) {
178        // Note: These are placeholder registrations - actual async handlers
179        // would need proper wrapper functions
180        // The actual commands are available as standalone functions
181    }
182}
183
184/// Global virtual commands enabled flag
185static VIRTUAL_COMMANDS_ENABLED: std::sync::atomic::AtomicBool =
186    std::sync::atomic::AtomicBool::new(true);
187
188/// Enable virtual commands
189pub fn enable_virtual_commands() {
190    VIRTUAL_COMMANDS_ENABLED.store(true, std::sync::atomic::Ordering::SeqCst);
191}
192
193/// Disable virtual commands
194pub fn disable_virtual_commands() {
195    VIRTUAL_COMMANDS_ENABLED.store(false, std::sync::atomic::Ordering::SeqCst);
196}
197
198/// Check if virtual commands are enabled
199pub fn are_virtual_commands_enabled() -> bool {
200    VIRTUAL_COMMANDS_ENABLED.load(std::sync::atomic::Ordering::SeqCst)
201}