minion_engine/config/
manager.rs1use std::collections::HashMap;
2
3use crate::config::merge::yaml_to_json;
4use crate::config::StepConfig;
5use crate::workflow::schema::{StepType, WorkflowConfig};
6
7pub struct ConfigManager {
9 config: WorkflowConfig,
10}
11
12impl ConfigManager {
13 pub fn new(config: WorkflowConfig) -> Self {
14 Self { config }
15 }
16
17 pub fn resolve(
20 &self,
21 step_name: &str,
22 step_type: &StepType,
23 step_inline: &HashMap<String, serde_yaml::Value>,
24 ) -> StepConfig {
25 let mut merged = HashMap::new();
26
27 for (k, v) in &self.config.global {
29 merged.insert(k.clone(), yaml_to_json(v));
30 }
31
32 let empty = HashMap::new();
34 let type_config = match step_type {
35 StepType::Agent => &self.config.agent,
36 StepType::Cmd => &self.config.cmd,
37 StepType::Chat => &self.config.chat,
38 StepType::Gate => &self.config.gate,
39 _ => &empty,
40 };
41 for (k, v) in type_config {
42 merged.insert(k.clone(), yaml_to_json(v));
43 }
44
45 for (pattern, values) in &self.config.patterns {
47 if let Ok(re) = regex::Regex::new(pattern) {
48 if re.is_match(step_name) {
49 for (k, v) in values {
50 merged.insert(k.clone(), yaml_to_json(v));
51 }
52 }
53 }
54 }
55
56 for (k, v) in step_inline {
58 merged.insert(k.clone(), yaml_to_json(v));
59 }
60
61 StepConfig { values: merged }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use std::time::Duration;
69
70 fn yaml_str(s: &str) -> serde_yaml::Value {
71 serde_yaml::Value::String(s.to_string())
72 }
73
74 #[test]
75 fn test_global_timeout_overridden_by_step_inline() {
76 let mut global = HashMap::new();
77 global.insert("timeout".to_string(), yaml_str("300s"));
78
79 let config = WorkflowConfig {
80 global,
81 ..Default::default()
82 };
83 let manager = ConfigManager::new(config);
84
85 let mut inline = HashMap::new();
86 inline.insert("timeout".to_string(), yaml_str("10s"));
87
88 let resolved = manager.resolve("any_step", &StepType::Agent, &inline);
89 assert_eq!(
90 resolved.get_duration("timeout"),
91 Some(Duration::from_secs(10)),
92 "step inline timeout=10s should override global timeout=300s"
93 );
94 }
95
96 #[test]
97 fn test_pattern_match_sets_model() {
98 let mut pattern_values = HashMap::new();
99 pattern_values.insert("model".to_string(), yaml_str("claude-3-haiku"));
100
101 let mut patterns = HashMap::new();
102 patterns.insert("lint.*".to_string(), pattern_values);
103
104 let config = WorkflowConfig {
105 patterns,
106 ..Default::default()
107 };
108 let manager = ConfigManager::new(config);
109
110 let resolved = manager.resolve("lint_check", &StepType::Agent, &HashMap::new());
111 assert_eq!(
112 resolved.get_str("model"),
113 Some("claude-3-haiku"),
114 "pattern 'lint.*' should match step name 'lint_check'"
115 );
116 }
117
118 #[test]
119 fn test_pattern_no_match() {
120 let mut pattern_values = HashMap::new();
121 pattern_values.insert("model".to_string(), yaml_str("claude-3-haiku"));
122
123 let mut patterns = HashMap::new();
124 patterns.insert("lint.*".to_string(), pattern_values);
125
126 let config = WorkflowConfig {
127 patterns,
128 ..Default::default()
129 };
130 let manager = ConfigManager::new(config);
131
132 let resolved = manager.resolve("test_run", &StepType::Agent, &HashMap::new());
133 assert_eq!(
134 resolved.get_str("model"),
135 None,
136 "pattern 'lint.*' should NOT match step name 'test_run'"
137 );
138 }
139}