ricecoder_commands/
registry.rs

1use crate::error::{CommandError, Result};
2use crate::types::CommandDefinition;
3use std::collections::HashMap;
4
5/// Registry for managing custom commands
6#[derive(Debug, Clone)]
7pub struct CommandRegistry {
8    commands: HashMap<String, CommandDefinition>,
9}
10
11impl CommandRegistry {
12    /// Create a new empty command registry
13    pub fn new() -> Self {
14        Self {
15            commands: HashMap::new(),
16        }
17    }
18
19    /// Register a command
20    pub fn register(&mut self, command: CommandDefinition) -> Result<()> {
21        if command.id.is_empty() {
22            return Err(CommandError::InvalidCommandName(
23                "Command ID cannot be empty".to_string(),
24            ));
25        }
26
27        if self.commands.contains_key(&command.id) {
28            return Err(CommandError::InvalidCommandName(format!(
29                "Command already registered: {}",
30                command.id
31            )));
32        }
33
34        self.commands.insert(command.id.clone(), command);
35        Ok(())
36    }
37
38    /// Unregister a command
39    pub fn unregister(&mut self, command_id: &str) -> Result<()> {
40        self.commands
41            .remove(command_id)
42            .ok_or_else(|| CommandError::CommandNotFound(command_id.to_string()))?;
43        Ok(())
44    }
45
46    /// Get a command by ID
47    pub fn get(&self, command_id: &str) -> Result<CommandDefinition> {
48        self.commands
49            .get(command_id)
50            .cloned()
51            .ok_or_else(|| CommandError::CommandNotFound(command_id.to_string()))
52    }
53
54    /// Get all commands
55    pub fn list_all(&self) -> Vec<CommandDefinition> {
56        self.commands.values().cloned().collect()
57    }
58
59    /// Get all enabled commands
60    pub fn list_enabled(&self) -> Vec<CommandDefinition> {
61        self.commands
62            .values()
63            .filter(|cmd| cmd.enabled)
64            .cloned()
65            .collect()
66    }
67
68    /// Get commands by tag
69    pub fn find_by_tag(&self, tag: &str) -> Vec<CommandDefinition> {
70        self.commands
71            .values()
72            .filter(|cmd| cmd.tags.contains(&tag.to_string()))
73            .cloned()
74            .collect()
75    }
76
77    /// Search commands by name or description
78    pub fn search(&self, query: &str) -> Vec<CommandDefinition> {
79        let query_lower = query.to_lowercase();
80        self.commands
81            .values()
82            .filter(|cmd| {
83                cmd.name.to_lowercase().contains(&query_lower)
84                    || cmd.description.to_lowercase().contains(&query_lower)
85            })
86            .cloned()
87            .collect()
88    }
89
90    /// Enable a command
91    pub fn enable(&mut self, command_id: &str) -> Result<()> {
92        let command = self
93            .commands
94            .get_mut(command_id)
95            .ok_or_else(|| CommandError::CommandNotFound(command_id.to_string()))?;
96        command.enabled = true;
97        Ok(())
98    }
99
100    /// Disable a command
101    pub fn disable(&mut self, command_id: &str) -> Result<()> {
102        let command = self
103            .commands
104            .get_mut(command_id)
105            .ok_or_else(|| CommandError::CommandNotFound(command_id.to_string()))?;
106        command.enabled = false;
107        Ok(())
108    }
109
110    /// Check if a command exists
111    pub fn exists(&self, command_id: &str) -> bool {
112        self.commands.contains_key(command_id)
113    }
114
115    /// Get the number of registered commands
116    pub fn count(&self) -> usize {
117        self.commands.len()
118    }
119
120    /// Clear all commands
121    pub fn clear(&mut self) {
122        self.commands.clear();
123    }
124}
125
126impl Default for CommandRegistry {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn test_register_command() {
138        let mut registry = CommandRegistry::new();
139        let cmd = CommandDefinition::new("test", "Test", "echo test");
140        assert!(registry.register(cmd).is_ok());
141        assert_eq!(registry.count(), 1);
142    }
143
144    #[test]
145    fn test_register_duplicate_command() {
146        let mut registry = CommandRegistry::new();
147        let cmd1 = CommandDefinition::new("test", "Test", "echo test");
148        let cmd2 = CommandDefinition::new("test", "Test", "echo test");
149        assert!(registry.register(cmd1).is_ok());
150        assert!(registry.register(cmd2).is_err());
151    }
152
153    #[test]
154    fn test_get_command() {
155        let mut registry = CommandRegistry::new();
156        let cmd = CommandDefinition::new("test", "Test", "echo test");
157        registry.register(cmd).unwrap();
158        let retrieved = registry.get("test").unwrap();
159        assert_eq!(retrieved.id, "test");
160    }
161
162    #[test]
163    fn test_get_nonexistent_command() {
164        let registry = CommandRegistry::new();
165        assert!(registry.get("nonexistent").is_err());
166    }
167
168    #[test]
169    fn test_unregister_command() {
170        let mut registry = CommandRegistry::new();
171        let cmd = CommandDefinition::new("test", "Test", "echo test");
172        registry.register(cmd).unwrap();
173        assert!(registry.unregister("test").is_ok());
174        assert_eq!(registry.count(), 0);
175    }
176
177    #[test]
178    fn test_list_all_commands() {
179        let mut registry = CommandRegistry::new();
180        registry
181            .register(CommandDefinition::new("cmd1", "Cmd1", "echo 1"))
182            .ok();
183        registry
184            .register(CommandDefinition::new("cmd2", "Cmd2", "echo 2"))
185            .ok();
186        assert_eq!(registry.list_all().len(), 2);
187    }
188
189    #[test]
190    fn test_list_enabled_commands() {
191        let mut registry = CommandRegistry::new();
192        let cmd1 = CommandDefinition::new("cmd1", "Cmd1", "echo 1");
193        let mut cmd2 = CommandDefinition::new("cmd2", "Cmd2", "echo 2");
194        cmd2.enabled = false;
195        registry.register(cmd1).ok();
196        registry.register(cmd2).ok();
197        assert_eq!(registry.list_enabled().len(), 1);
198    }
199
200    #[test]
201    fn test_find_by_tag() {
202        let mut registry = CommandRegistry::new();
203        let cmd1 = CommandDefinition::new("cmd1", "Cmd1", "echo 1").with_tag("test");
204        let cmd2 = CommandDefinition::new("cmd2", "Cmd2", "echo 2").with_tag("prod");
205        registry.register(cmd1).ok();
206        registry.register(cmd2).ok();
207        assert_eq!(registry.find_by_tag("test").len(), 1);
208    }
209
210    #[test]
211    fn test_search_commands() {
212        let mut registry = CommandRegistry::new();
213        let cmd1 = CommandDefinition::new("cmd1", "Test Command", "echo 1");
214        let cmd2 = CommandDefinition::new("cmd2", "Other", "echo 2");
215        registry.register(cmd1).ok();
216        registry.register(cmd2).ok();
217        assert_eq!(registry.search("test").len(), 1);
218    }
219
220    #[test]
221    fn test_enable_disable() {
222        let mut registry = CommandRegistry::new();
223        let cmd = CommandDefinition::new("test", "Test", "echo test");
224        registry.register(cmd).ok();
225        registry.disable("test").ok();
226        assert!(!registry.get("test").unwrap().enabled);
227        registry.enable("test").ok();
228        assert!(registry.get("test").unwrap().enabled);
229    }
230}