1use crate::command::Command;
2use std::fs;
3use std::path::Path;
4use serde::Deserialize;
5
6pub trait CommandSource {
7 fn load_commands(&self) -> Vec<Box<dyn Command>>;
8}
9
10#[derive(Debug, Deserialize)]
12struct JsonCommand {
13 name: String,
14 #[serde(default)]
15 aliases: Vec<String>,
16 #[serde(default)]
17 help: Option<String>,
18 #[serde(default)]
19 hidden: bool,
20}
21
22struct DynamicJsonCommand {
24 name: String,
25 #[allow(dead_code)]
26 aliases: Vec<String>,
27 help: Option<String>,
28 hidden: bool,
29}
30
31impl Command for DynamicJsonCommand {
32 fn name(&self) -> &str {
33 &self.name
34 }
35
36 fn aliases(&self) -> &[&str] {
37 &[]
38 }
39
40 fn help(&self) -> Option<&str> {
41 self.help.as_deref()
42 }
43
44 fn hidden(&self) -> bool {
45 self.hidden
46 }
47
48 fn validate(&self, _args: &[String]) -> Result<(), String> {
49 Ok(())
50 }
51
52 fn execute(&self, args: &[String]) {
53 println!("Executed dynamic command '{}': {:?}", self.name, args);
54 }
55}
56
57pub struct JsonFileSource {
59 path: String,
60}
61
62impl JsonFileSource {
63 pub fn new<P: Into<String>>(path: P) -> Self {
64 Self { path: path.into() }
65 }
66}
67
68impl CommandSource for JsonFileSource {
69 fn load_commands(&self) -> Vec<Box<dyn Command>> {
70 let path = Path::new(&self.path);
71 let data = fs::read_to_string(path).expect("Failed to read JSON");
72 let defs: Vec<JsonCommand> = serde_json::from_str(&data).expect("Invalid JSON");
73
74 defs.into_iter()
75 .map(|cmd| {
76 Box::new(DynamicJsonCommand {
77 name: cmd.name,
78 aliases: cmd.aliases,
79 help: cmd.help,
80 hidden: cmd.hidden,
81 }) as Box<dyn Command>
82 })
83 .collect()
84 }
85}