modcli/
loader.rs

1#[cfg(feature = "custom-commands")]
2//pub mod custom;
3
4#[cfg(feature = "custom-commands")]
5//use crate::custom::CustomCommand;
6
7#[cfg(feature = "plugins")]
8pub mod plugins;
9
10#[cfg(feature = "plugins")]
11use crate::loader::plugins::PluginLoader;
12
13#[cfg(feature = "internal-commands")]
14use crate::commands::{
15    PingCommand, 
16    HelloCommand, 
17    ShellCommand,
18    HelpCommand,
19    FrameworkCommand
20};
21use crate::output::hook;
22
23use std::collections::HashMap;
24use crate::command::Command;
25use crate::loader::sources::CommandSource;
26pub mod sources;
27
28pub struct CommandRegistry {
29    prefix: String,
30    commands: HashMap<String, Box<dyn Command>>,
31}
32impl CommandRegistry {
33
34    /// Creates a new command registry
35    pub fn new() -> Self {
36        let mut reg = Self {
37            prefix: String::new(),
38            commands: HashMap::new(),
39        };
40
41        #[cfg(feature = "custom-commands")]
42        reg.load_custom_commands();
43
44        #[cfg(feature = "internal-commands")]
45        reg.load_internal_commands();
46
47        reg
48    }
49
50    /// Sets the command prefix
51    pub fn set_prefix(&mut self, prefix: &str) {
52        self.prefix = prefix.to_string();
53    }
54
55    /// Gets the command prefix
56    pub fn get_prefix(&self) -> &str {
57        &self.prefix
58    }
59 
60    /// Gets a command by name
61    pub fn get(&self, name: &str) -> Option<&Box<dyn Command>> {
62        self.commands.get(name)
63    }
64
65    /// Gets a command by name with prefix
66    pub fn register(&mut self, cmd: Box<dyn Command>) {
67        self.commands.insert(cmd.name().to_string(), cmd);
68    }
69
70    /// Returns all registered commands (read-only)
71    pub fn all(&self) -> impl Iterator<Item = &Box<dyn Command>> {
72        self.commands.values()
73    }
74
75    /// Registers a command with an alias
76    #[cfg(feature = "plugins")]
77    pub fn load_plugins(&mut self, path: &str) {
78        let loader = PluginLoader::new(path);
79        for plugin in loader.load_plugins() {
80            self.register(plugin);
81        }
82    }
83
84    pub fn execute(&self, cmd: &str, args: &[String]) {
85        if let Some(command) = self.commands.get(cmd) {
86            if command.name() == "help" {
87                // Special case for help: render help output with registry context
88                if args.len() > 1 {
89                    hook::error("Invalid usage: Too many arguments. Usage: help [command]");
90                    return;
91                }
92
93                if args.len() == 1 {
94                    let query = &args[0];
95                    if let Some(target) = self.commands.get(query) {
96                        if target.hidden() {
97                            println!("No help available for '{}'", query);
98                        } else {
99                            println!(
100                                "{} - {}",
101                                target.name(),
102                                target.help().unwrap_or("No description.")
103                            );
104                        }
105                    } else {
106                        let unknown = format!("[{}]. Type `help` or `--help` for a list of available commands.", query);
107                        hook::unknown(&unknown);
108                    }
109                    return;
110                }
111
112                println!("Help:");
113                for command in self.commands.values() {
114                    if !command.hidden() {
115                        println!(
116                            "  {:<12} {}",
117                            command.name(),
118                            command.help().unwrap_or("No description")
119                        );
120                    }
121                }
122            } else {
123                // Normal command execution
124                if let Err(err) = command.validate(args) {
125                    let err_msg = format!("Invalid usage: {}", err);
126                    hook::error(&err_msg);
127                    command.execute(args);
128                    return;
129                }
130
131                command.execute(args);
132            }
133        } else {
134            let unknown = format!("[{}]. Type `help` or `--help` for a list of available commands.", cmd);
135            hook::unknown(&unknown);
136        }
137    }
138
139
140    #[cfg(feature = "internal-commands")]
141    pub fn load_internal_commands(&mut self) {
142        self.register(Box::new(PingCommand));
143        self.register(Box::new(HelloCommand));
144        self.register(Box::new(ShellCommand));
145        self.register(Box::new(FrameworkCommand));
146        self.register(Box::new(HelpCommand::new()));
147    }
148
149
150
151    pub fn load_from(&mut self, source: Box<dyn CommandSource>) {
152        for cmd in source.load_commands() {
153            self.register(cmd);
154        }
155    }
156
157
158    pub fn len(&self) -> usize {
159        self.commands.len()
160    }
161
162    #[cfg(feature = "custom-commands")]
163    pub fn load_custom_commands(&mut self) {
164       //self.register(Box::new(CustomCommand));
165    }
166
167}