dynamic_cli/registry/
command_registry.rs

1//! Command registry implementation
2//!
3//! This module provides the central registry for storing and retrieving
4//! command definitions and their associated handlers.
5//!
6//! # Architecture
7//!
8//! The registry maintains two main data structures:
9//! - A map of command names to their definitions and handlers
10//! - A map of aliases to canonical command names
11//!
12//! This design allows O(1) lookup by both command name and alias.
13//!
14//! # Example
15//!
16//! ```
17//! use dynamic_cli::registry::CommandRegistry;
18//! use dynamic_cli::config::schema::CommandDefinition;
19//! use dynamic_cli::executor::CommandHandler;
20//! use std::collections::HashMap;
21//!
22//! // Create a registry
23//! let mut registry = CommandRegistry::new();
24//!
25//! // Define a command
26//! let definition = CommandDefinition {
27//!     name: "hello".to_string(),
28//!     aliases: vec!["hi".to_string(), "greet".to_string()],
29//!     description: "Say hello".to_string(),
30//!     required: false,
31//!     arguments: vec![],
32//!     options: vec![],
33//!     implementation: "hello_handler".to_string(),
34//! };
35//!
36//! // Create a handler
37//! struct HelloCommand;
38//! impl CommandHandler for HelloCommand {
39//!     fn execute(
40//!         &self,
41//!         _ctx: &mut dyn dynamic_cli::context::ExecutionContext,
42//!         _args: &HashMap<String, String>,
43//!     ) -> dynamic_cli::Result<()> {
44//!         println!("Hello!");
45//!         Ok(())
46//!     }
47//! }
48//!
49//! // Register the command
50//! registry.register(definition, Box::new(HelloCommand))?;
51//!
52//! // Retrieve by name
53//! assert!(registry.get_handler("hello").is_some());
54//!
55//! // Retrieve by alias
56//! assert_eq!(registry.resolve_name("hi"), Some("hello"));
57//! # Ok::<(), dynamic_cli::error::DynamicCliError>(())
58//! ```
59
60use crate::config::schema::CommandDefinition;
61use crate::error::{RegistryError, Result};
62use crate::executor::CommandHandler;
63use std::collections::HashMap;
64
65/// Central registry for commands and their handlers
66///
67/// The registry stores all registered commands along with their definitions
68/// and handlers. It provides efficient lookup by both command name and alias.
69///
70/// # Thread Safety
71///
72/// The registry is designed to be constructed once during application startup
73/// and then shared immutably across the application. For multi-threaded access,
74/// wrap it in `Arc<CommandRegistry>`.
75///
76/// # Example
77///
78/// ```
79/// use dynamic_cli::registry::CommandRegistry;
80/// use dynamic_cli::config::schema::CommandDefinition;
81/// use dynamic_cli::executor::CommandHandler;
82/// use std::collections::HashMap;
83///
84/// let mut registry = CommandRegistry::new();
85///
86/// // Register commands during initialization
87/// # let definition = CommandDefinition {
88/// #     name: "test".to_string(),
89/// #     aliases: vec![],
90/// #     description: "Test".to_string(),
91/// #     required: false,
92/// #     arguments: vec![],
93/// #     options: vec![],
94/// #     implementation: "test_handler".to_string(),
95/// # };
96/// # struct TestCommand;
97/// # impl CommandHandler for TestCommand {
98/// #     fn execute(&self, _: &mut dyn dynamic_cli::context::ExecutionContext, _: &HashMap<String, String>) -> dynamic_cli::Result<()> { Ok(()) }
99/// # }
100/// registry.register(definition, Box::new(TestCommand))?;
101///
102/// // Use throughout the application
103/// if let Some(handler) = registry.get_handler("test") {
104///     // Execute the command
105/// }
106/// # Ok::<(), dynamic_cli::error::DynamicCliError>(())
107/// ```
108pub struct CommandRegistry {
109    /// Map of command names to their data
110    /// Key: canonical command name
111    /// Value: (CommandDefinition, Box<dyn CommandHandler>)
112    commands: HashMap<String, (CommandDefinition, Box<dyn CommandHandler>)>,
113
114    /// Map of aliases to canonical command names
115    /// Key: alias
116    /// Value: canonical command name
117    ///
118    /// This allows O(1) resolution of aliases to command names.
119    aliases: HashMap<String, String>,
120}
121
122impl CommandRegistry {
123    /// Create a new empty registry
124    ///
125    /// # Example
126    ///
127    /// ```
128    /// use dynamic_cli::registry::CommandRegistry;
129    ///
130    /// let registry = CommandRegistry::new();
131    /// assert_eq!(registry.list_commands().len(), 0);
132    /// ```
133    pub fn new() -> Self {
134        Self {
135            commands: HashMap::new(),
136            aliases: HashMap::new(),
137        }
138    }
139
140    /// Register a command with its handler
141    ///
142    /// This method registers a command definition along with its handler.
143    /// It also registers all aliases for the command.
144    ///
145    /// # Arguments
146    ///
147    /// * `definition` - The command definition from the configuration
148    /// * `handler` - The handler implementation for this command
149    ///
150    /// # Returns
151    ///
152    /// - `Ok(())` if registration succeeds
153    /// - `Err(RegistryError)` if:
154    ///   - A command with the same name is already registered
155    ///   - An alias conflicts with an existing command or alias
156    ///
157    /// # Errors
158    ///
159    /// - [`RegistryError::DuplicateRegistration`] if the command name already exists
160    /// - [`RegistryError::DuplicateAlias`] if an alias is already in use
161    ///
162    /// # Example
163    ///
164    /// ```
165    /// use dynamic_cli::registry::CommandRegistry;
166    /// use dynamic_cli::config::schema::CommandDefinition;
167    /// use dynamic_cli::executor::CommandHandler;
168    /// use std::collections::HashMap;
169    ///
170    /// let mut registry = CommandRegistry::new();
171    ///
172    /// let definition = CommandDefinition {
173    ///     name: "simulate".to_string(),
174    ///     aliases: vec!["sim".to_string(), "run".to_string()],
175    ///     description: "Run simulation".to_string(),
176    ///     required: false,
177    ///     arguments: vec![],
178    ///     options: vec![],
179    ///     implementation: "sim_handler".to_string(),
180    /// };
181    ///
182    /// struct SimCommand;
183    /// impl CommandHandler for SimCommand {
184    ///     fn execute(
185    ///         &self,
186    ///         _: &mut dyn dynamic_cli::context::ExecutionContext,
187    ///         _: &HashMap<String, String>,
188    ///     ) -> dynamic_cli::Result<()> {
189    ///         Ok(())
190    ///     }
191    /// }
192    ///
193    /// // Register the command
194    /// registry.register(definition, Box::new(SimCommand))?;
195    ///
196    /// // Can now access by name or alias
197    /// assert!(registry.get_handler("simulate").is_some());
198    /// assert_eq!(registry.resolve_name("sim"), Some("simulate"));
199    /// # Ok::<(), dynamic_cli::error::DynamicCliError>(())
200    /// ```
201    pub fn register(
202        &mut self,
203        definition: CommandDefinition,
204        handler: Box<dyn CommandHandler>,
205    ) -> Result<()> {
206        let cmd_name = &definition.name;
207
208        // Check if command name is already registered
209        if self.commands.contains_key(cmd_name) {
210            return Err(RegistryError::DuplicateRegistration {
211                name: cmd_name.clone(),
212            }
213            .into());
214        }
215
216        // Check if command name conflicts with existing alias
217        if self.aliases.contains_key(cmd_name) {
218            let existing_cmd = self.aliases.get(cmd_name).unwrap();
219            return Err(RegistryError::DuplicateAlias {
220                alias: cmd_name.clone(),
221                existing_command: existing_cmd.clone(),
222            }
223            .into());
224        }
225
226        // Check all aliases for conflicts
227        for alias in &definition.aliases {
228            // Check if alias conflicts with existing command name
229            if self.commands.contains_key(alias) {
230                return Err(RegistryError::DuplicateAlias {
231                    alias: alias.clone(),
232                    existing_command: alias.clone(),
233                }
234                .into());
235            }
236
237            // Check if alias conflicts with existing alias
238            if self.aliases.contains_key(alias) {
239                let existing_cmd = self.aliases.get(alias).unwrap();
240                return Err(RegistryError::DuplicateAlias {
241                    alias: alias.clone(),
242                    existing_command: existing_cmd.clone(),
243                }
244                .into());
245            }
246        }
247
248        // Register all aliases
249        for alias in &definition.aliases {
250            self.aliases.insert(alias.clone(), cmd_name.clone());
251        }
252
253        // Register the command
254        self.commands
255            .insert(cmd_name.clone(), (definition, handler));
256
257        Ok(())
258    }
259
260    /// Resolve a name (command or alias) to the canonical command name
261    ///
262    /// This method checks if the given name is either:
263    /// - A registered command name (returns the name itself)
264    /// - An alias (returns the canonical command name)
265    ///
266    /// # Arguments
267    ///
268    /// * `name` - The name or alias to resolve
269    ///
270    /// # Returns
271    ///
272    /// - `Some(&str)` - The canonical command name
273    /// - `None` - If the name is not registered
274    ///
275    /// # Example
276    ///
277    /// ```
278    /// use dynamic_cli::registry::CommandRegistry;
279    /// # use dynamic_cli::config::schema::CommandDefinition;
280    /// # use dynamic_cli::executor::CommandHandler;
281    /// # use std::collections::HashMap;
282    ///
283    /// let mut registry = CommandRegistry::new();
284    ///
285    /// # let definition = CommandDefinition {
286    /// #     name: "hello".to_string(),
287    /// #     aliases: vec!["hi".to_string()],
288    /// #     description: "".to_string(),
289    /// #     required: false,
290    /// #     arguments: vec![],
291    /// #     options: vec![],
292    /// #     implementation: "".to_string(),
293    /// # };
294    /// # struct TestCmd;
295    /// # impl CommandHandler for TestCmd {
296    /// #     fn execute(&self, _: &mut dyn dynamic_cli::context::ExecutionContext, _: &HashMap<String, String>) -> dynamic_cli::Result<()> { Ok(()) }
297    /// # }
298    /// # registry.register(definition, Box::new(TestCmd)).unwrap();
299    /// // Resolve command name
300    /// assert_eq!(registry.resolve_name("hello"), Some("hello"));
301    ///
302    /// // Resolve alias
303    /// assert_eq!(registry.resolve_name("hi"), Some("hello"));
304    ///
305    /// // Unknown name
306    /// assert_eq!(registry.resolve_name("unknown"), None);
307    /// ```
308    pub fn resolve_name(&self, name: &str) -> Option<&str> {
309        // First check if it's a command name
310        // Return reference to the stored name, not the parameter
311        if let Some((cmd_def, _)) = self.commands.get(name) {
312            return Some(cmd_def.name.as_str());
313        }
314
315        // Then check if it's an alias
316        self.aliases.get(name).map(|s| s.as_str())
317    }
318
319    /// Get the definition of a command by name or alias
320    ///
321    /// # Arguments
322    ///
323    /// * `name` - The command name or alias
324    ///
325    /// # Returns
326    ///
327    /// - `Some(&CommandDefinition)` if the command exists
328    /// - `None` if the command is not registered
329    ///
330    /// # Example
331    ///
332    /// ```
333    /// # use dynamic_cli::registry::CommandRegistry;
334    /// # use dynamic_cli::config::schema::CommandDefinition;
335    /// # use dynamic_cli::executor::CommandHandler;
336    /// # use std::collections::HashMap;
337    /// # let mut registry = CommandRegistry::new();
338    /// # let definition = CommandDefinition {
339    /// #     name: "test".to_string(),
340    /// #     aliases: vec!["t".to_string()],
341    /// #     description: "Test command".to_string(),
342    /// #     required: false,
343    /// #     arguments: vec![],
344    /// #     options: vec![],
345    /// #     implementation: "".to_string(),
346    /// # };
347    /// # struct TestCmd;
348    /// # impl CommandHandler for TestCmd {
349    /// #     fn execute(&self, _: &mut dyn dynamic_cli::context::ExecutionContext, _: &HashMap<String, String>) -> dynamic_cli::Result<()> { Ok(()) }
350    /// # }
351    /// # registry.register(definition, Box::new(TestCmd)).unwrap();
352    /// // Get by name
353    /// if let Some(def) = registry.get_definition("test") {
354    ///     assert_eq!(def.name, "test");
355    ///     assert_eq!(def.description, "Test command");
356    /// }
357    ///
358    /// // Get by alias
359    /// if let Some(def) = registry.get_definition("t") {
360    ///     assert_eq!(def.name, "test");
361    /// }
362    /// ```
363    pub fn get_definition(&self, name: &str) -> Option<&CommandDefinition> {
364        let canonical_name = self.resolve_name(name)?;
365        self.commands.get(canonical_name).map(|(def, _)| def)
366    }
367
368    /// Get the handler of a command by name or alias
369    ///
370    /// This is the primary method used during command execution to
371    /// retrieve the handler that will execute the command.
372    ///
373    /// # Arguments
374    ///
375    /// * `name` - The command name or alias
376    ///
377    /// # Returns
378    ///
379    /// - `Some(&Box<dyn CommandHandler>)` if the command exists
380    /// - `None` if the command is not registered
381    ///
382    /// # Example
383    ///
384    /// ```
385    /// # use dynamic_cli::registry::CommandRegistry;
386    /// # use dynamic_cli::config::schema::CommandDefinition;
387    /// # use dynamic_cli::executor::CommandHandler;
388    /// # use std::collections::HashMap;
389    /// # let mut registry = CommandRegistry::new();
390    /// # let definition = CommandDefinition {
391    /// #     name: "exec".to_string(),
392    /// #     aliases: vec!["x".to_string()],
393    /// #     description: "".to_string(),
394    /// #     required: false,
395    /// #     arguments: vec![],
396    /// #     options: vec![],
397    /// #     implementation: "".to_string(),
398    /// # };
399    /// # struct ExecCmd;
400    /// # impl CommandHandler for ExecCmd {
401    /// #     fn execute(&self, _: &mut dyn dynamic_cli::context::ExecutionContext, _: &HashMap<String, String>) -> dynamic_cli::Result<()> { Ok(()) }
402    /// # }
403    /// # registry.register(definition, Box::new(ExecCmd)).unwrap();
404    /// // Get handler by name
405    /// if let Some(handler) = registry.get_handler("exec") {
406    ///     // Use handler for execution
407    /// }
408    ///
409    /// // Get handler by alias
410    /// if let Some(handler) = registry.get_handler("x") {
411    ///     // Same handler
412    /// }
413    /// ```
414    pub fn get_handler(&self, name: &str) -> Option<&Box<dyn CommandHandler>> {
415        let canonical_name = self.resolve_name(name)?;
416        self.commands
417            .get(canonical_name)
418            .map(|(_, handler)| handler)
419    }
420
421    /// List all registered command definitions
422    ///
423    /// Returns a vector of references to all command definitions in the registry.
424    /// The order is not guaranteed.
425    ///
426    /// # Returns
427    ///
428    /// Vector of command definition references
429    ///
430    /// # Example
431    ///
432    /// ```
433    /// # use dynamic_cli::registry::CommandRegistry;
434    /// # use dynamic_cli::config::schema::CommandDefinition;
435    /// # use dynamic_cli::executor::CommandHandler;
436    /// # use std::collections::HashMap;
437    /// # let mut registry = CommandRegistry::new();
438    /// # let def1 = CommandDefinition {
439    /// #     name: "cmd1".to_string(),
440    /// #     aliases: vec![],
441    /// #     description: "".to_string(),
442    /// #     required: false,
443    /// #     arguments: vec![],
444    /// #     options: vec![],
445    /// #     implementation: "".to_string(),
446    /// # };
447    /// # let def2 = CommandDefinition {
448    /// #     name: "cmd2".to_string(),
449    /// #     aliases: vec![],
450    /// #     description: "".to_string(),
451    /// #     required: false,
452    /// #     arguments: vec![],
453    /// #     options: vec![],
454    /// #     implementation: "".to_string(),
455    /// # };
456    /// # struct TestCmd;
457    /// # impl CommandHandler for TestCmd {
458    /// #     fn execute(&self, _: &mut dyn dynamic_cli::context::ExecutionContext, _: &HashMap<String, String>) -> dynamic_cli::Result<()> { Ok(()) }
459    /// # }
460    /// # registry.register(def1, Box::new(TestCmd)).unwrap();
461    /// # registry.register(def2, Box::new(TestCmd)).unwrap();
462    /// let commands = registry.list_commands();
463    /// assert_eq!(commands.len(), 2);
464    ///
465    /// // Use for help text, command completion, etc.
466    /// for cmd in commands {
467    ///     println!("{}: {}", cmd.name, cmd.description);
468    /// }
469    /// ```
470    pub fn list_commands(&self) -> Vec<&CommandDefinition> {
471        self.commands.values().map(|(def, _)| def).collect()
472    }
473
474    /// Get the number of registered commands
475    ///
476    /// # Example
477    ///
478    /// ```
479    /// use dynamic_cli::registry::CommandRegistry;
480    ///
481    /// let registry = CommandRegistry::new();
482    /// assert_eq!(registry.len(), 0);
483    /// ```
484    pub fn len(&self) -> usize {
485        self.commands.len()
486    }
487
488    /// Check if the registry is empty
489    ///
490    /// # Example
491    ///
492    /// ```
493    /// use dynamic_cli::registry::CommandRegistry;
494    ///
495    /// let registry = CommandRegistry::new();
496    /// assert!(registry.is_empty());
497    /// ```
498    pub fn is_empty(&self) -> bool {
499        self.commands.is_empty()
500    }
501
502    /// Check if a command is registered (by name or alias)
503    ///
504    /// # Example
505    ///
506    /// ```
507    /// # use dynamic_cli::registry::CommandRegistry;
508    /// # use dynamic_cli::config::schema::CommandDefinition;
509    /// # use dynamic_cli::executor::CommandHandler;
510    /// # use std::collections::HashMap;
511    /// # let mut registry = CommandRegistry::new();
512    /// # let definition = CommandDefinition {
513    /// #     name: "test".to_string(),
514    /// #     aliases: vec!["t".to_string()],
515    /// #     description: "".to_string(),
516    /// #     required: false,
517    /// #     arguments: vec![],
518    /// #     options: vec![],
519    /// #     implementation: "".to_string(),
520    /// # };
521    /// # struct TestCmd;
522    /// # impl CommandHandler for TestCmd {
523    /// #     fn execute(&self, _: &mut dyn dynamic_cli::context::ExecutionContext, _: &HashMap<String, String>) -> dynamic_cli::Result<()> { Ok(()) }
524    /// # }
525    /// # registry.register(definition, Box::new(TestCmd)).unwrap();
526    /// assert!(registry.contains("test"));
527    /// assert!(registry.contains("t"));
528    /// assert!(!registry.contains("unknown"));
529    /// ```
530    pub fn contains(&self, name: &str) -> bool {
531        self.resolve_name(name).is_some()
532    }
533}
534
535// Implement Default for convenience
536impl Default for CommandRegistry {
537    fn default() -> Self {
538        Self::new()
539    }
540}
541
542#[cfg(test)]
543mod tests {
544    use super::*;
545    use std::any::Any;
546
547    // Test fixtures
548    #[derive(Default)]
549    struct TestContext;
550
551    impl crate::context::ExecutionContext for TestContext {
552        fn as_any(&self) -> &dyn Any {
553            self
554        }
555        fn as_any_mut(&mut self) -> &mut dyn Any {
556            self
557        }
558    }
559
560    struct TestHandler;
561
562    impl CommandHandler for TestHandler {
563        fn execute(
564            &self,
565            _context: &mut dyn crate::context::ExecutionContext,
566            _args: &HashMap<String, String>,
567        ) -> crate::error::Result<()> {
568            Ok(())
569        }
570    }
571
572    fn create_test_definition(name: &str, aliases: Vec<&str>) -> CommandDefinition {
573        CommandDefinition {
574            name: name.to_string(),
575            aliases: aliases.iter().map(|s| s.to_string()).collect(),
576            description: format!("{} command", name),
577            required: false,
578            arguments: vec![],
579            options: vec![],
580            implementation: format!("{}_handler", name),
581        }
582    }
583
584    // Basic functionality tests
585    #[test]
586    fn test_new_registry_is_empty() {
587        let registry = CommandRegistry::new();
588        assert!(registry.is_empty());
589        assert_eq!(registry.len(), 0);
590        assert_eq!(registry.list_commands().len(), 0);
591    }
592
593    #[test]
594    fn test_register_command() {
595        let mut registry = CommandRegistry::new();
596        let definition = create_test_definition("test", vec![]);
597
598        let result = registry.register(definition, Box::new(TestHandler));
599
600        assert!(result.is_ok());
601        assert_eq!(registry.len(), 1);
602        assert!(!registry.is_empty());
603    }
604
605    #[test]
606    fn test_register_command_with_aliases() {
607        let mut registry = CommandRegistry::new();
608        let definition = create_test_definition("hello", vec!["hi", "greet"]);
609
610        registry
611            .register(definition, Box::new(TestHandler))
612            .unwrap();
613
614        assert_eq!(registry.len(), 1);
615        assert!(registry.contains("hello"));
616        assert!(registry.contains("hi"));
617        assert!(registry.contains("greet"));
618    }
619
620    #[test]
621    fn test_register_duplicate_command_fails() {
622        let mut registry = CommandRegistry::new();
623        let def1 = create_test_definition("test", vec![]);
624        let def2 = create_test_definition("test", vec![]);
625
626        registry.register(def1, Box::new(TestHandler)).unwrap();
627        let result = registry.register(def2, Box::new(TestHandler));
628
629        assert!(result.is_err());
630        match result.unwrap_err() {
631            crate::error::DynamicCliError::Registry(RegistryError::DuplicateRegistration {
632                name,
633            }) => {
634                assert_eq!(name, "test");
635            }
636            _ => panic!("Wrong error type"),
637        }
638    }
639
640    #[test]
641    fn test_register_duplicate_alias_fails() {
642        let mut registry = CommandRegistry::new();
643        let def1 = create_test_definition("cmd1", vec!["c"]);
644        let def2 = create_test_definition("cmd2", vec!["c"]);
645
646        registry.register(def1, Box::new(TestHandler)).unwrap();
647        let result = registry.register(def2, Box::new(TestHandler));
648
649        assert!(result.is_err());
650        match result.unwrap_err() {
651            crate::error::DynamicCliError::Registry(RegistryError::DuplicateAlias {
652                alias,
653                existing_command,
654            }) => {
655                assert_eq!(alias, "c");
656                assert_eq!(existing_command, "cmd1");
657            }
658            _ => panic!("Wrong error type"),
659        }
660    }
661
662    #[test]
663    fn test_alias_conflicts_with_command_name() {
664        let mut registry = CommandRegistry::new();
665        let def1 = create_test_definition("test", vec![]);
666        let def2 = create_test_definition("other", vec!["test"]);
667
668        registry.register(def1, Box::new(TestHandler)).unwrap();
669        let result = registry.register(def2, Box::new(TestHandler));
670
671        assert!(result.is_err());
672    }
673
674    #[test]
675    fn test_command_name_conflicts_with_alias() {
676        let mut registry = CommandRegistry::new();
677        let def1 = create_test_definition("cmd1", vec!["other"]);
678        let def2 = create_test_definition("other", vec![]);
679
680        registry.register(def1, Box::new(TestHandler)).unwrap();
681        let result = registry.register(def2, Box::new(TestHandler));
682
683        assert!(result.is_err());
684    }
685
686    // Resolve name tests
687    #[test]
688    fn test_resolve_command_name() {
689        let mut registry = CommandRegistry::new();
690        let definition = create_test_definition("test", vec![]);
691
692        registry
693            .register(definition, Box::new(TestHandler))
694            .unwrap();
695
696        assert_eq!(registry.resolve_name("test"), Some("test"));
697    }
698
699    #[test]
700    fn test_resolve_alias() {
701        let mut registry = CommandRegistry::new();
702        let definition = create_test_definition("hello", vec!["hi", "greet"]);
703
704        registry
705            .register(definition, Box::new(TestHandler))
706            .unwrap();
707
708        assert_eq!(registry.resolve_name("hi"), Some("hello"));
709        assert_eq!(registry.resolve_name("greet"), Some("hello"));
710    }
711
712    #[test]
713    fn test_resolve_unknown_name() {
714        let registry = CommandRegistry::new();
715        assert_eq!(registry.resolve_name("unknown"), None);
716    }
717
718    // Get definition tests
719    #[test]
720    fn test_get_definition_by_name() {
721        let mut registry = CommandRegistry::new();
722        let definition = create_test_definition("test", vec![]);
723
724        registry
725            .register(definition, Box::new(TestHandler))
726            .unwrap();
727
728        let retrieved = registry.get_definition("test");
729        assert!(retrieved.is_some());
730        assert_eq!(retrieved.unwrap().name, "test");
731    }
732
733    #[test]
734    fn test_get_definition_by_alias() {
735        let mut registry = CommandRegistry::new();
736        let definition = create_test_definition("hello", vec!["hi"]);
737
738        registry
739            .register(definition, Box::new(TestHandler))
740            .unwrap();
741
742        let retrieved = registry.get_definition("hi");
743        assert!(retrieved.is_some());
744        assert_eq!(retrieved.unwrap().name, "hello");
745    }
746
747    #[test]
748    fn test_get_definition_unknown() {
749        let registry = CommandRegistry::new();
750        assert!(registry.get_definition("unknown").is_none());
751    }
752
753    // Get handler tests
754    #[test]
755    fn test_get_handler_by_name() {
756        let mut registry = CommandRegistry::new();
757        let definition = create_test_definition("test", vec![]);
758
759        registry
760            .register(definition, Box::new(TestHandler))
761            .unwrap();
762
763        let handler = registry.get_handler("test");
764        assert!(handler.is_some());
765    }
766
767    #[test]
768    fn test_get_handler_by_alias() {
769        let mut registry = CommandRegistry::new();
770        let definition = create_test_definition("hello", vec!["hi"]);
771
772        registry
773            .register(definition, Box::new(TestHandler))
774            .unwrap();
775
776        let handler = registry.get_handler("hi");
777        assert!(handler.is_some());
778    }
779
780    #[test]
781    fn test_get_handler_unknown() {
782        let registry = CommandRegistry::new();
783        assert!(registry.get_handler("unknown").is_none());
784    }
785
786    // List commands tests
787    #[test]
788    fn test_list_commands_empty() {
789        let registry = CommandRegistry::new();
790        let commands = registry.list_commands();
791        assert_eq!(commands.len(), 0);
792    }
793
794    #[test]
795    fn test_list_commands_multiple() {
796        let mut registry = CommandRegistry::new();
797
798        registry
799            .register(
800                create_test_definition("cmd1", vec![]),
801                Box::new(TestHandler),
802            )
803            .unwrap();
804        registry
805            .register(
806                create_test_definition("cmd2", vec![]),
807                Box::new(TestHandler),
808            )
809            .unwrap();
810        registry
811            .register(
812                create_test_definition("cmd3", vec![]),
813                Box::new(TestHandler),
814            )
815            .unwrap();
816
817        let commands = registry.list_commands();
818        assert_eq!(commands.len(), 3);
819
820        let names: Vec<&str> = commands.iter().map(|c| c.name.as_str()).collect();
821        assert!(names.contains(&"cmd1"));
822        assert!(names.contains(&"cmd2"));
823        assert!(names.contains(&"cmd3"));
824    }
825
826    // Integration tests
827    #[test]
828    fn test_complete_workflow() {
829        let mut registry = CommandRegistry::new();
830
831        // Register multiple commands with aliases
832        let def1 = create_test_definition("simulate", vec!["sim", "run"]);
833        let def2 = create_test_definition("validate", vec!["val", "check"]);
834        let def3 = create_test_definition("help", vec!["h", "?"]);
835
836        registry.register(def1, Box::new(TestHandler)).unwrap();
837        registry.register(def2, Box::new(TestHandler)).unwrap();
838        registry.register(def3, Box::new(TestHandler)).unwrap();
839
840        // Verify registry state
841        assert_eq!(registry.len(), 3);
842
843        // Verify all names resolve correctly
844        assert_eq!(registry.resolve_name("simulate"), Some("simulate"));
845        assert_eq!(registry.resolve_name("sim"), Some("simulate"));
846        assert_eq!(registry.resolve_name("validate"), Some("validate"));
847        assert_eq!(registry.resolve_name("val"), Some("validate"));
848
849        // Verify handlers are accessible
850        assert!(registry.get_handler("simulate").is_some());
851        assert!(registry.get_handler("sim").is_some());
852        assert!(registry.get_handler("h").is_some());
853
854        // Verify definitions are accessible
855        let sim_def = registry.get_definition("sim");
856        assert!(sim_def.is_some());
857        assert_eq!(sim_def.unwrap().name, "simulate");
858    }
859
860    #[test]
861    fn test_default_trait() {
862        let registry: CommandRegistry = Default::default();
863        assert!(registry.is_empty());
864    }
865
866    #[test]
867    fn test_contains_method() {
868        let mut registry = CommandRegistry::new();
869        let definition = create_test_definition("test", vec!["t"]);
870
871        registry
872            .register(definition, Box::new(TestHandler))
873            .unwrap();
874
875        assert!(registry.contains("test"));
876        assert!(registry.contains("t"));
877        assert!(!registry.contains("unknown"));
878    }
879
880    #[test]
881    fn test_multiple_aliases_same_command() {
882        let mut registry = CommandRegistry::new();
883        let definition = create_test_definition("command", vec!["c", "cmd", "com"]);
884
885        registry
886            .register(definition, Box::new(TestHandler))
887            .unwrap();
888
889        // All aliases should resolve to the same command
890        assert_eq!(registry.resolve_name("c"), Some("command"));
891        assert_eq!(registry.resolve_name("cmd"), Some("command"));
892        assert_eq!(registry.resolve_name("com"), Some("command"));
893
894        // All should return the same handler
895        let handler1 = registry.get_handler("c");
896        let handler2 = registry.get_handler("cmd");
897        assert!(handler1.is_some());
898        assert!(handler2.is_some());
899    }
900
901    #[test]
902    fn test_case_sensitivity() {
903        let mut registry = CommandRegistry::new();
904        let definition = create_test_definition("Test", vec![]);
905
906        registry
907            .register(definition, Box::new(TestHandler))
908            .unwrap();
909
910        // Case matters
911        assert!(registry.contains("Test"));
912        assert!(!registry.contains("test"));
913        assert!(!registry.contains("TEST"));
914    }
915
916    #[test]
917    fn test_empty_alias_list() {
918        let mut registry = CommandRegistry::new();
919        let definition = create_test_definition("test", vec![]);
920
921        let result = registry.register(definition, Box::new(TestHandler));
922
923        assert!(result.is_ok());
924        assert!(registry.contains("test"));
925    }
926}