modcli/
config.rs

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    /// Loads config from: custom path > project root > examples/ > embedded
51    pub fn load(_unused: Option<&str>) -> &'static CliConfig {
52        CONFIG.get_or_init(|| {
53            // 👇 Custom override path if set
54            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            // Fallbacks...
61            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()); // only sets once
80}