ralph_workflow/cli/reducer/
apply.rs1use super::state::CliState;
7use crate::config::{Config, ReviewDepth, Verbosity};
8
9#[must_use]
27pub fn apply_cli_state_to_config(cli_state: &CliState, config: &Config) -> Config {
28 let verbosity = if cli_state.debug_mode {
31 Verbosity::Debug
32 } else if cli_state.full_mode {
33 Verbosity::Full
34 } else if cli_state.quiet_mode {
35 Verbosity::Quiet
36 } else {
37 cli_state
38 .verbosity
39 .map(Verbosity::from)
40 .unwrap_or(config.verbosity)
41 };
42
43 let current_developer_iters = config.developer_iters;
45 let current_reviewer_reviews = config.reviewer_reviews;
46 let developer_iters = cli_state.resolved_developer_iters(current_developer_iters);
47 let reviewer_reviews = cli_state.resolved_reviewer_reviews(current_reviewer_reviews);
48
49 let developer_agent = cli_state
51 .developer_agent
52 .clone()
53 .or_else(|| config.developer_agent.clone());
54 let reviewer_agent = cli_state
55 .reviewer_agent
56 .clone()
57 .or_else(|| config.reviewer_agent.clone());
58
59 let developer_model = cli_state
61 .developer_model
62 .clone()
63 .or_else(|| config.developer_model.clone());
64 let reviewer_model = cli_state
65 .reviewer_model
66 .clone()
67 .or_else(|| config.reviewer_model.clone());
68 let developer_provider = cli_state
69 .developer_provider
70 .clone()
71 .or_else(|| config.developer_provider.clone());
72 let reviewer_provider = cli_state
73 .reviewer_provider
74 .clone()
75 .or_else(|| config.reviewer_provider.clone());
76 let reviewer_json_parser = cli_state
77 .reviewer_json_parser
78 .clone()
79 .or_else(|| config.reviewer_json_parser.clone());
80
81 let isolation_mode = cli_state.isolation_mode.unwrap_or(config.isolation_mode);
84
85 let review_depth = cli_state
87 .review_depth
88 .as_ref()
89 .and_then(|d| ReviewDepth::from_str(d))
90 .unwrap_or(config.review_depth);
91
92 let git_user_name = cli_state
94 .git_user_name
95 .clone()
96 .or_else(|| config.git_user_name.clone());
97 let git_user_email = cli_state
98 .git_user_email
99 .clone()
100 .or_else(|| config.git_user_email.clone());
101
102 let show_streaming_metrics = config.show_streaming_metrics || cli_state.streaming_metrics;
104
105 let (developer_agent, reviewer_agent) = if let Some(ref preset) = cli_state.agent_preset {
108 if preset.as_str() == "opencode" {
109 (Some("opencode".to_string()), Some("opencode".to_string()))
110 } else {
111 (developer_agent, reviewer_agent)
112 }
113 } else {
114 (developer_agent, reviewer_agent)
115 };
116
117 Config {
119 verbosity,
120 developer_iters,
121 reviewer_reviews,
122 developer_agent,
123 reviewer_agent,
124 developer_model,
125 reviewer_model,
126 developer_provider,
127 reviewer_provider,
128 reviewer_json_parser,
129 isolation_mode,
130 review_depth,
131 git_user_name,
132 git_user_email,
133 show_streaming_metrics,
134 ..config.clone()
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::config::types::{BehavioralFlags, FeatureFlags};
142
143 fn create_test_config() -> Config {
144 Config {
145 developer_agent: None,
146 reviewer_agent: None,
147 developer_cmd: None,
148 reviewer_cmd: None,
149 commit_cmd: None,
150 developer_model: None,
151 reviewer_model: None,
152 developer_provider: None,
153 reviewer_provider: None,
154 reviewer_json_parser: None,
155 features: FeatureFlags {
156 checkpoint_enabled: true,
157 force_universal_prompt: false,
158 },
159 developer_iters: 5,
160 reviewer_reviews: 2,
161 fast_check_cmd: None,
162 full_check_cmd: None,
163 behavior: BehavioralFlags {
164 interactive: true,
165 auto_detect_stack: true,
166 strict_validation: false,
167 },
168 prompt_path: std::path::PathBuf::from(".agent/last_prompt.txt"),
169 user_templates_dir: None,
170 developer_context: 1,
171 reviewer_context: 0,
172 verbosity: Verbosity::Verbose,
173 review_depth: ReviewDepth::Standard,
174 isolation_mode: true,
175 git_user_name: None,
176 git_user_email: None,
177 show_streaming_metrics: false,
178 review_format_retries: 5,
179 max_dev_continuations: Some(2),
180 max_xsd_retries: Some(10),
181 max_same_agent_retries: Some(2),
182 max_commit_residual_retries: Some(10),
183 execution_history_limit: 1000,
184 cloud: crate::config::types::CloudConfig::disabled(),
185 }
186 }
187
188 #[test]
189 fn test_apply_verbosity_debug() {
190 let cli_state = CliState {
191 debug_mode: true,
192 ..Default::default()
193 };
194
195 let config = create_test_config();
196 let result = apply_cli_state_to_config(&cli_state, &config);
197
198 assert_eq!(result.verbosity, Verbosity::Debug);
199 }
200
201 #[test]
202 fn test_apply_verbosity_full() {
203 let cli_state = CliState {
204 full_mode: true,
205 ..Default::default()
206 };
207
208 let config = create_test_config();
209 let result = apply_cli_state_to_config(&cli_state, &config);
210
211 assert_eq!(result.verbosity, Verbosity::Full);
212 }
213
214 #[test]
215 fn test_apply_verbosity_quiet() {
216 let cli_state = CliState {
217 quiet_mode: true,
218 ..Default::default()
219 };
220
221 let config = create_test_config();
222 let result = apply_cli_state_to_config(&cli_state, &config);
223
224 assert_eq!(result.verbosity, Verbosity::Quiet);
225 }
226
227 #[test]
228 fn test_apply_verbosity_explicit() {
229 let cli_state = CliState {
230 verbosity: Some(3),
231 ..Default::default()
232 };
233
234 let config = create_test_config();
235 let result = apply_cli_state_to_config(&cli_state, &config);
236
237 assert_eq!(result.verbosity, Verbosity::Full); }
239
240 #[test]
241 fn test_apply_iters_from_preset() {
242 use super::super::state::PresetType;
243
244 let cli_state = CliState {
245 preset_applied: Some(PresetType::Long),
246 ..Default::default()
247 };
248
249 let config = Config {
250 developer_iters: 5,
251 reviewer_reviews: 2,
252 ..create_test_config()
253 };
254
255 let result = apply_cli_state_to_config(&cli_state, &config);
256
257 assert_eq!(result.developer_iters, 15);
258 assert_eq!(result.reviewer_reviews, 10);
259 }
260
261 #[test]
262 fn test_apply_iters_explicit_override_preset() {
263 use super::super::state::PresetType;
264
265 let cli_state = CliState {
266 preset_applied: Some(PresetType::Quick), developer_iters: Some(7), reviewer_reviews: Some(3), ..Default::default()
270 };
271
272 let config = create_test_config();
273 let result = apply_cli_state_to_config(&cli_state, &config);
274
275 assert_eq!(result.developer_iters, 7);
277 assert_eq!(result.reviewer_reviews, 3);
278 }
279
280 #[test]
281 fn test_apply_developer_agent() {
282 let cli_state = CliState {
283 developer_agent: Some("claude".to_string()),
284 ..Default::default()
285 };
286
287 let config = create_test_config();
288 let result = apply_cli_state_to_config(&cli_state, &config);
289
290 assert_eq!(result.developer_agent, Some("claude".to_string()));
291 }
292
293 #[test]
294 fn test_apply_reviewer_agent() {
295 let cli_state = CliState {
296 reviewer_agent: Some("gpt".to_string()),
297 ..Default::default()
298 };
299
300 let config = create_test_config();
301 let result = apply_cli_state_to_config(&cli_state, &config);
302
303 assert_eq!(result.reviewer_agent, Some("gpt".to_string()));
304 }
305
306 #[test]
307 fn test_apply_isolation_mode_disabled() {
308 let cli_state = CliState {
309 isolation_mode: Some(false),
310 ..Default::default()
311 };
312
313 let config = Config {
314 isolation_mode: true,
315 ..create_test_config()
316 };
317
318 let result = apply_cli_state_to_config(&cli_state, &config);
319
320 assert!(!result.isolation_mode);
321 }
322
323 #[test]
324 fn test_apply_review_depth() {
325 let cli_state = CliState {
326 review_depth: Some("comprehensive".to_string()),
327 ..Default::default()
328 };
329
330 let config = create_test_config();
331 let result = apply_cli_state_to_config(&cli_state, &config);
332
333 assert_eq!(result.review_depth, ReviewDepth::Comprehensive);
334 }
335
336 #[test]
337 fn test_apply_git_identity() {
338 let cli_state = CliState {
339 git_user_name: Some("John Doe".to_string()),
340 git_user_email: Some("john@example.com".to_string()),
341 ..Default::default()
342 };
343
344 let config = create_test_config();
345 let result = apply_cli_state_to_config(&cli_state, &config);
346
347 assert_eq!(result.git_user_name, Some("John Doe".to_string()));
348 assert_eq!(result.git_user_email, Some("john@example.com".to_string()));
349 }
350
351 #[test]
352 fn test_apply_streaming_metrics() {
353 let cli_state = CliState {
354 streaming_metrics: true,
355 ..Default::default()
356 };
357
358 let config = create_test_config();
359 let result = apply_cli_state_to_config(&cli_state, &config);
360
361 assert!(result.show_streaming_metrics);
362 }
363
364 #[test]
365 fn test_apply_agent_preset_opencode() {
366 let cli_state = CliState {
367 agent_preset: Some("opencode".to_string()),
368 ..Default::default()
369 };
370
371 let config = create_test_config();
372 let result = apply_cli_state_to_config(&cli_state, &config);
373
374 assert_eq!(result.developer_agent, Some("opencode".to_string()));
375 assert_eq!(result.reviewer_agent, Some("opencode".to_string()));
376 }
377
378 #[test]
379 fn test_apply_agent_preset_default() {
380 let cli_state = CliState {
381 agent_preset: Some("default".to_string()),
382 ..Default::default()
383 };
384
385 let config = Config {
386 developer_agent: Some("existing-dev".to_string()),
387 reviewer_agent: Some("existing-rev".to_string()),
388 ..create_test_config()
389 };
390
391 let result = apply_cli_state_to_config(&cli_state, &config);
392
393 assert_eq!(result.developer_agent, Some("existing-dev".to_string()));
395 assert_eq!(result.reviewer_agent, Some("existing-rev".to_string()));
396 }
397
398 #[test]
399 fn test_apply_preserves_unrelated_config_fields() {
400 let cli_state = CliState {
401 developer_agent: Some("new-agent".to_string()),
402 ..Default::default()
403 };
404
405 let config = Config {
406 isolation_mode: true,
407 review_depth: ReviewDepth::Comprehensive,
408 ..create_test_config()
409 };
410
411 let result = apply_cli_state_to_config(&cli_state, &config);
412
413 assert_eq!(result.developer_agent, Some("new-agent".to_string()));
415 assert!(result.isolation_mode);
416 assert_eq!(result.review_depth, ReviewDepth::Comprehensive);
417 }
418}