1#[cfg(feature = "custom-commands")]
2#[cfg(feature = "custom-commands")]
5#[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 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 pub fn set_prefix(&mut self, prefix: &str) {
52 self.prefix = prefix.to_string();
53 }
54
55 pub fn get_prefix(&self) -> &str {
57 &self.prefix
58 }
59
60 pub fn get(&self, name: &str) -> Option<&Box<dyn Command>> {
62 self.commands.get(name)
63 }
64
65 pub fn register(&mut self, cmd: Box<dyn Command>) {
67 self.commands.insert(cmd.name().to_string(), cmd);
68 }
69
70 pub fn all(&self) -> impl Iterator<Item = &Box<dyn Command>> {
72 self.commands.values()
73 }
74
75 #[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 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 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 }
166
167}