Skip to main content

hojicha_core/debug/
config.rs

1//! Debug configuration from environment variables
2
3use super::TraceLevel;
4use std::env;
5
6/// Debug configuration
7#[derive(Debug, Clone)]
8pub struct DebugConfig {
9    /// Whether debugging is enabled
10    pub enabled: bool,
11    /// Trace level for output
12    pub trace_level: TraceLevel,
13    /// Whether to collect performance metrics
14    pub collect_metrics: bool,
15    /// Whether to show debug overlay (future feature)
16    pub show_overlay: bool,
17    /// Output format
18    pub format: OutputFormat,
19}
20
21/// Output format for debug information
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum OutputFormat {
24    /// Human-readable text
25    Text,
26    /// JSON format (for tooling)
27    Json,
28    /// Compact format
29    Compact,
30}
31
32impl DebugConfig {
33    /// Create configuration from environment variables
34    ///
35    /// Environment variables:
36    /// - `HOJICHA_DEBUG`: Enable debugging (1, true, yes)
37    /// - `HOJICHA_TRACE`: Trace level (commands,messages,events,metrics,all)
38    /// - `HOJICHA_METRICS`: Enable metrics collection (1, true, yes)
39    /// - `HOJICHA_DEBUG_FORMAT`: Output format (text, json, compact)
40    #[must_use]
41    pub fn from_env() -> Self {
42        let enabled = env::var("HOJICHA_DEBUG")
43            .map(|v| matches!(v.to_lowercase().as_str(), "1" | "true" | "yes"))
44            .unwrap_or(false);
45
46        let trace_level = env::var("HOJICHA_TRACE")
47            .map_or_else(
48                |_| {
49                    if enabled {
50                        TraceLevel::COMMANDS.combine(TraceLevel::MESSAGES)
51                    } else {
52                        TraceLevel::NONE
53                    }
54                },
55                |v| TraceLevel::parse(&v),
56            );
57
58        let collect_metrics = env::var("HOJICHA_METRICS")
59            .map(|v| matches!(v.to_lowercase().as_str(), "1" | "true" | "yes"))
60            .unwrap_or(enabled);
61
62        let format = env::var("HOJICHA_DEBUG_FORMAT")
63            .ok()
64            .and_then(|v| match v.to_lowercase().as_str() {
65                "json" => Some(OutputFormat::Json),
66                "compact" => Some(OutputFormat::Compact),
67                "text" => Some(OutputFormat::Text),
68                _ => None,
69            })
70            .unwrap_or(OutputFormat::Text);
71
72        Self {
73            enabled,
74            trace_level,
75            collect_metrics,
76            show_overlay: false, // Future feature
77            format,
78        }
79    }
80
81    /// Create a default configuration with debugging disabled
82    pub fn disabled() -> Self {
83        Self {
84            enabled: false,
85            trace_level: TraceLevel::NONE,
86            collect_metrics: false,
87            show_overlay: false,
88            format: OutputFormat::Text,
89        }
90    }
91
92    /// Create a configuration with all debugging enabled
93    pub fn full_debug() -> Self {
94        Self {
95            enabled: true,
96            trace_level: TraceLevel::ALL,
97            collect_metrics: true,
98            show_overlay: false,
99            format: OutputFormat::Text,
100        }
101    }
102
103    /// Builder method to enable debugging
104    pub fn with_debugging(mut self, enabled: bool) -> Self {
105        self.enabled = enabled;
106        self
107    }
108
109    /// Builder method to set trace level
110    pub fn with_trace_level(mut self, level: TraceLevel) -> Self {
111        self.trace_level = level;
112        self
113    }
114
115    /// Builder method to enable metrics
116    pub fn with_metrics(mut self, enabled: bool) -> Self {
117        self.collect_metrics = enabled;
118        self
119    }
120
121    /// Builder method to set output format
122    pub fn with_format(mut self, format: OutputFormat) -> Self {
123        self.format = format;
124        self
125    }
126}
127
128impl Default for DebugConfig {
129    fn default() -> Self {
130        Self::from_env()
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use std::env;
138
139    /// Helper to set and restore environment variables for testing
140    struct EnvGuard {
141        vars: Vec<(String, Option<String>)>,
142    }
143
144    impl EnvGuard {
145        fn new(vars: &[(&str, &str)]) -> Self {
146            let mut guard_vars = Vec::new();
147            for (key, value) in vars {
148                let old_value = env::var(key).ok();
149                env::set_var(key, value);
150                guard_vars.push((key.to_string(), old_value));
151            }
152            Self { vars: guard_vars }
153        }
154    }
155
156    impl Drop for EnvGuard {
157        fn drop(&mut self) {
158            for (key, old_value) in &self.vars {
159                match old_value {
160                    Some(val) => env::set_var(key, val),
161                    None => env::remove_var(key),
162                }
163            }
164        }
165    }
166
167    /// Behavioral test: Environment variable parsing with different values
168    #[test]
169    fn test_env_var_parsing_variations() {
170        // Test "1" as true
171        let _guard1 = EnvGuard::new(&[("HOJICHA_DEBUG", "1")]);
172        let config1 = DebugConfig::from_env();
173        assert!(config1.enabled);
174
175        // Test "false" as false
176        let _guard2 = EnvGuard::new(&[("HOJICHA_DEBUG", "false")]);
177        let config2 = DebugConfig::from_env();
178        assert!(!config2.enabled);
179
180        // Test "YES" (case insensitive) as true
181        let _guard3 = EnvGuard::new(&[("HOJICHA_DEBUG", "YES")]);
182        let config3 = DebugConfig::from_env();
183        assert!(config3.enabled);
184    }
185}