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 test;
24mod touch;
25mod r#true;
26mod which;
27mod yes;
28
29pub use basename::basename;
30pub use cat::cat;
31pub use cd::cd;
32pub use cp::cp;
33pub use dirname::dirname;
34pub use echo::echo;
35pub use env::env;
36pub use exit::exit;
37pub use ls::ls;
38pub use mkdir::mkdir;
39pub use mv::mv;
40pub use pwd::pwd;
41pub use r#false::r#false;
42pub use r#true::r#true;
43pub use rm::rm;
44pub use seq::seq;
45pub use sleep::sleep;
46pub use test::test;
47pub use touch::touch;
48pub use which::which;
49pub use yes::yes;
50
51use crate::utils::CommandResult;
52use std::collections::HashMap;
53use tokio::sync::mpsc;
54
55/// Context for virtual command execution
56pub struct CommandContext {
57    /// Command arguments (excluding the command name)
58    pub args: Vec<String>,
59    /// Standard input content
60    pub stdin: Option<String>,
61    /// Current working directory
62    pub cwd: Option<std::path::PathBuf>,
63    /// Environment variables
64    pub env: Option<HashMap<String, String>>,
65    /// Channel to send streaming output
66    pub output_tx: Option<mpsc::Sender<StreamChunk>>,
67    /// Cancellation check function
68    pub is_cancelled: Option<Box<dyn Fn() -> bool + Send + Sync>>,
69}
70
71impl std::fmt::Debug for CommandContext {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.debug_struct("CommandContext")
74            .field("args", &self.args)
75            .field("stdin", &self.stdin)
76            .field("cwd", &self.cwd)
77            .field("env", &self.env)
78            .field("output_tx", &self.output_tx.is_some())
79            .field("is_cancelled", &self.is_cancelled.is_some())
80            .finish()
81    }
82}
83
84/// A chunk of streaming output
85#[derive(Debug, Clone)]
86pub enum StreamChunk {
87    Stdout(String),
88    Stderr(String),
89}
90
91impl CommandContext {
92    /// Create a new command context with arguments
93    pub fn new(args: Vec<String>) -> Self {
94        CommandContext {
95            args,
96            stdin: None,
97            cwd: None,
98            env: None,
99            output_tx: None,
100            is_cancelled: None,
101        }
102    }
103
104    /// Check if the command has been cancelled
105    pub fn is_cancelled(&self) -> bool {
106        self.is_cancelled.as_ref().map(|f| f()).unwrap_or(false)
107    }
108
109    /// Get the current working directory
110    pub fn get_cwd(&self) -> std::path::PathBuf {
111        self.cwd.clone().unwrap_or_else(|| {
112            std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("/"))
113        })
114    }
115}
116
117/// Type for virtual command handler functions
118pub type VirtualCommandHandler =
119    fn(
120        CommandContext,
121    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = CommandResult> + Send>>;
122
123/// Registry of virtual commands
124pub struct VirtualCommandRegistry {
125    commands: HashMap<String, VirtualCommandHandler>,
126}
127
128impl Default for VirtualCommandRegistry {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134impl VirtualCommandRegistry {
135    /// Create a new empty registry
136    pub fn new() -> Self {
137        VirtualCommandRegistry {
138            commands: HashMap::new(),
139        }
140    }
141
142    /// Create a registry with all built-in commands registered
143    pub fn with_builtins() -> Self {
144        let mut registry = Self::new();
145        registry.register_builtins();
146        registry
147    }
148
149    /// Register a virtual command
150    pub fn register(&mut self, name: &str, handler: VirtualCommandHandler) {
151        self.commands.insert(name.to_string(), handler);
152    }
153
154    /// Unregister a virtual command
155    pub fn unregister(&mut self, name: &str) -> bool {
156        self.commands.remove(name).is_some()
157    }
158
159    /// Get a virtual command handler
160    pub fn get(&self, name: &str) -> Option<&VirtualCommandHandler> {
161        self.commands.get(name)
162    }
163
164    /// Check if a command is registered
165    pub fn contains(&self, name: &str) -> bool {
166        self.commands.contains_key(name)
167    }
168
169    /// List all registered command names
170    pub fn list(&self) -> Vec<&str> {
171        self.commands.keys().map(|s| s.as_str()).collect()
172    }
173
174    /// Register all built-in commands
175    pub fn register_builtins(&mut self) {
176        // Note: These are placeholder registrations - actual async handlers
177        // would need proper wrapper functions
178        // The actual commands are available as standalone functions
179    }
180}
181
182/// Global virtual commands enabled flag
183static VIRTUAL_COMMANDS_ENABLED: std::sync::atomic::AtomicBool =
184    std::sync::atomic::AtomicBool::new(true);
185
186/// Enable virtual commands
187pub fn enable_virtual_commands() {
188    VIRTUAL_COMMANDS_ENABLED.store(true, std::sync::atomic::Ordering::SeqCst);
189}
190
191/// Disable virtual commands
192pub fn disable_virtual_commands() {
193    VIRTUAL_COMMANDS_ENABLED.store(false, std::sync::atomic::Ordering::SeqCst);
194}
195
196/// Check if virtual commands are enabled
197pub fn are_virtual_commands_enabled() -> bool {
198    VIRTUAL_COMMANDS_ENABLED.load(std::sync::atomic::Ordering::SeqCst)
199}