1use serde::Deserialize;
2use std::sync::OnceLock;
3use std::fs;
4
5static CONFIG: OnceLock<CliConfig> = OnceLock::new();
6static CONFIG_PATH: OnceLock<String> = OnceLock::new();
7static RAW_CONFIG: &str = include_str!("../examples/config.json");
8
9#[derive(Debug, Deserialize)]
10pub struct CliConfig {
11 pub modcli: ModCliSection,
12}
13
14#[derive(Debug, Deserialize)]
15pub struct ModCliSection {
16 pub name: Option<String>,
17 pub prefix: Option<String>,
18 pub banner: Option<String>,
19 pub theme: Option<String>,
20 pub delay: Option<u64>,
21 pub strict: Option<bool>,
22 pub force_shell: Option<bool>,
23 pub shell: Option<ShellConfig>,
24 pub messages: Option<MessageConfig>,
25}
26
27#[derive(Debug, Deserialize)]
28pub struct ShellConfig {
29 pub prompt: Option<String>,
30 pub welcome: Option<Vec<String>>,
31 pub goodbye: Option<Vec<String>>,
32}
33
34#[derive(Debug, Deserialize)]
35pub struct MessageConfig {
36 pub no_command: Option<String>,
37 pub not_found: Option<String>,
38}
39
40impl Default for MessageConfig {
41 fn default() -> Self {
42 MessageConfig {
43 no_command: Some("⚠️ No command given. Try `help`.".to_string()),
44 not_found: Some("⚠️ Command not found.".to_string()),
45 }
46 }
47}
48
49impl CliConfig {
50 pub fn load(_unused: Option<&str>) -> &'static CliConfig {
52 CONFIG.get_or_init(|| {
53 if let Some(p) = CONFIG_PATH.get() {
55 if let Ok(data) = fs::read_to_string(p) {
56 return parse(&data);
57 }
58 }
59
60 if let Ok(data) = fs::read_to_string("config.json") {
62 return parse(&data);
63 }
64
65 if let Ok(data) = fs::read_to_string("examples/config.json") {
66 return parse(&data);
67 }
68
69 parse(RAW_CONFIG)
70 })
71 }
72}
73
74fn parse(data: &str) -> CliConfig {
75 serde_json::from_str(data).expect("Failed to parse CLI config JSON")
76}
77
78pub fn set_path(path: &str) {
79 let _ = CONFIG_PATH.set(path.to_string()); }