Skip to main content

ralph_workflow/config/
mod.rs

1//! Configuration Module
2//!
3//! Handles environment variables and configuration for Ralph.
4//!
5//! # Key Types
6//!
7//! - [`Config`] - Runtime configuration used during pipeline execution
8//! - [`Verbosity`] - Output verbosity levels (Quiet, Normal, Verbose, Full, Debug)
9//! - [`ReviewDepth`] - Review thoroughness (Standard, Comprehensive, Security, Incremental)
10//! - [`UnifiedConfig`] - Full configuration file representation
11//!
12//! # Configuration Sources
13//!
14//! Ralph configuration is loaded from (in order of priority):
15//!
16//! 1. `~/.config/ralph-workflow.toml` (global config)
17//! 2. `.agent/ralph-workflow.toml` (local config, overrides global)
18//! 3. Environment variables (`RALPH_*`) as overrides
19//! 4. CLI arguments (final override)
20//!
21//! # Module Structure
22//!
23//! - [`types`] - Core configuration types (Config, ReviewDepth, Verbosity)
24//! - [`truncation`] - Truncation limits for verbosity levels
25//! - [`parser`] - Environment variable parsing (legacy)
26//! - [`unified`] - Unified configuration format types
27//! - [`loader`] - Unified configuration loader with env overrides
28//! - [`path_resolver`] - Configuration path resolution with dependency injection
29
30pub mod loader;
31pub mod parser;
32pub mod path_resolver;
33pub mod truncation;
34pub mod types;
35pub mod unified;
36pub mod validation;
37
38// Re-export main types at module level for convenience
39pub use types::{Config, ReviewDepth, Verbosity};
40
41// Re-export unified config types for --init-global handling
42pub use unified::{
43    unified_config_path, CcsAliasConfig, CcsConfig, ConfigInitResult as UnifiedConfigInitResult,
44    UnifiedConfig,
45};
46
47// Re-export config environment types for dependency injection
48pub use path_resolver::{ConfigEnvironment, MemoryConfigEnvironment, RealConfigEnvironment};
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_verbosity_from_u8() {
56        assert_eq!(Verbosity::from(0), Verbosity::Quiet);
57        assert_eq!(Verbosity::from(1), Verbosity::Normal);
58        assert_eq!(Verbosity::from(2), Verbosity::Verbose);
59        assert_eq!(Verbosity::from(3), Verbosity::Full);
60        assert_eq!(Verbosity::from(4), Verbosity::Debug);
61        assert_eq!(Verbosity::from(100), Verbosity::Debug);
62    }
63
64    #[test]
65    fn test_truncate_limits() {
66        // Quiet has reduced limits
67        assert_eq!(Verbosity::Quiet.truncate_limit("text"), 80);
68        assert_eq!(Verbosity::Quiet.truncate_limit("tool_input"), 40);
69
70        // Normal has conservative limits for manageable output
71        assert_eq!(Verbosity::Normal.truncate_limit("text"), 1000);
72        assert_eq!(Verbosity::Normal.truncate_limit("tool_input"), 300);
73        assert_eq!(Verbosity::Normal.truncate_limit("tool_result"), 500);
74
75        // Verbose (default) has conservative limits for reasonable output
76        assert_eq!(Verbosity::Verbose.truncate_limit("text"), 2000);
77        assert_eq!(Verbosity::Verbose.truncate_limit("tool_input"), 300);
78        assert_eq!(Verbosity::Verbose.truncate_limit("tool_result"), 500);
79
80        // Full and Debug have unlimited
81        assert_eq!(Verbosity::Full.truncate_limit("text"), 999_999);
82        assert_eq!(Verbosity::Debug.truncate_limit("text"), 999_999);
83    }
84
85    #[test]
86    fn test_verbosity_helpers() {
87        assert!(!Verbosity::Quiet.is_debug());
88        assert!(!Verbosity::Normal.is_debug());
89        assert!(!Verbosity::Verbose.is_debug());
90        assert!(!Verbosity::Full.is_debug());
91        assert!(Verbosity::Debug.is_debug());
92
93        assert!(!Verbosity::Quiet.is_verbose());
94        assert!(!Verbosity::Normal.is_verbose());
95        assert!(Verbosity::Verbose.is_verbose());
96        assert!(Verbosity::Full.is_verbose());
97        assert!(Verbosity::Debug.is_verbose());
98
99        // show_tool_input: true for Normal and above, false for Quiet
100        assert!(!Verbosity::Quiet.show_tool_input());
101        assert!(Verbosity::Normal.show_tool_input());
102        assert!(Verbosity::Verbose.show_tool_input());
103        assert!(Verbosity::Full.show_tool_input());
104        assert!(Verbosity::Debug.show_tool_input());
105    }
106
107    #[test]
108    fn test_review_depth_from_str() {
109        // Standard aliases
110        assert_eq!(
111            ReviewDepth::from_str("standard"),
112            Some(ReviewDepth::Standard)
113        );
114        assert_eq!(
115            ReviewDepth::from_str("default"),
116            Some(ReviewDepth::Standard)
117        );
118        assert_eq!(ReviewDepth::from_str("normal"), Some(ReviewDepth::Standard));
119
120        // Comprehensive aliases
121        assert_eq!(
122            ReviewDepth::from_str("comprehensive"),
123            Some(ReviewDepth::Comprehensive)
124        );
125        assert_eq!(
126            ReviewDepth::from_str("thorough"),
127            Some(ReviewDepth::Comprehensive)
128        );
129        assert_eq!(
130            ReviewDepth::from_str("full"),
131            Some(ReviewDepth::Comprehensive)
132        );
133
134        // Security aliases
135        assert_eq!(
136            ReviewDepth::from_str("security"),
137            Some(ReviewDepth::Security)
138        );
139        assert_eq!(ReviewDepth::from_str("secure"), Some(ReviewDepth::Security));
140        assert_eq!(
141            ReviewDepth::from_str("security-focused"),
142            Some(ReviewDepth::Security)
143        );
144
145        // Incremental aliases
146        assert_eq!(
147            ReviewDepth::from_str("incremental"),
148            Some(ReviewDepth::Incremental)
149        );
150        assert_eq!(
151            ReviewDepth::from_str("diff"),
152            Some(ReviewDepth::Incremental)
153        );
154        assert_eq!(
155            ReviewDepth::from_str("changed"),
156            Some(ReviewDepth::Incremental)
157        );
158
159        // Case insensitivity
160        assert_eq!(
161            ReviewDepth::from_str("SECURITY"),
162            Some(ReviewDepth::Security)
163        );
164        assert_eq!(
165            ReviewDepth::from_str("Comprehensive"),
166            Some(ReviewDepth::Comprehensive)
167        );
168
169        // Invalid values
170        assert_eq!(ReviewDepth::from_str("invalid"), None);
171        assert_eq!(ReviewDepth::from_str(""), None);
172    }
173
174    #[test]
175    fn test_review_depth_default() {
176        assert_eq!(ReviewDepth::default(), ReviewDepth::Standard);
177    }
178
179    #[test]
180    fn test_review_depth_description() {
181        assert!(ReviewDepth::Standard.description().contains("Balanced"));
182        assert!(ReviewDepth::Comprehensive
183            .description()
184            .contains("In-depth"));
185        assert!(ReviewDepth::Security.description().contains("OWASP"));
186        assert!(ReviewDepth::Incremental.description().contains("git diff"));
187    }
188}