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