ralph_workflow/cli/reducer/
parser.rs1use super::event::CliEvent;
7
8#[must_use]
36pub fn args_to_events(args: &super::super::Args) -> Vec<CliEvent> {
37 let verbosity_events = std::iter::empty()
38 .chain(
39 args.verbosity_shorthand
40 .quiet
41 .then_some(CliEvent::QuietModeEnabled),
42 )
43 .chain(
44 args.verbosity_shorthand
45 .full
46 .then_some(CliEvent::FullModeEnabled),
47 )
48 .chain(
49 args.debug_verbosity
50 .debug
51 .then_some(CliEvent::DebugModeEnabled),
52 )
53 .chain(args.verbosity.map(|level| CliEvent::VerbositySet { level }));
54
55 let preset_events = std::iter::empty()
56 .chain(
57 args.quick_presets
58 .quick
59 .then_some(CliEvent::QuickPresetApplied),
60 )
61 .chain(
62 args.quick_presets
63 .rapid
64 .then_some(CliEvent::RapidPresetApplied),
65 )
66 .chain(
67 args.quick_presets
68 .long
69 .then_some(CliEvent::LongPresetApplied),
70 )
71 .chain(
72 args.standard_presets
73 .standard
74 .then_some(CliEvent::StandardPresetApplied),
75 )
76 .chain(
77 args.standard_presets
78 .thorough
79 .then_some(CliEvent::ThoroughPresetApplied),
80 );
81
82 let iteration_events = std::iter::empty()
83 .chain(
84 args.developer_iters
85 .map(|v| CliEvent::DeveloperItersSet { value: v }),
86 )
87 .chain(
88 args.reviewer_reviews
89 .map(|v| CliEvent::ReviewerReviewsSet { value: v }),
90 );
91
92 let agent_events = std::iter::empty()
93 .chain(
94 args.developer_agent
95 .clone()
96 .map(|a| CliEvent::DeveloperAgentSet { agent: a }),
97 )
98 .chain(
99 args.reviewer_agent
100 .clone()
101 .map(|a| CliEvent::ReviewerAgentSet { agent: a }),
102 )
103 .chain(
104 args.developer_model
105 .clone()
106 .map(|m| CliEvent::DeveloperModelSet { model: m }),
107 )
108 .chain(
109 args.reviewer_model
110 .clone()
111 .map(|m| CliEvent::ReviewerModelSet { model: m }),
112 )
113 .chain(
114 args.developer_provider
115 .clone()
116 .map(|p| CliEvent::DeveloperProviderSet { provider: p }),
117 )
118 .chain(
119 args.reviewer_provider
120 .clone()
121 .map(|p| CliEvent::ReviewerProviderSet { provider: p }),
122 )
123 .chain(
124 args.reviewer_json_parser
125 .clone()
126 .map(|p| CliEvent::ReviewerJsonParserSet { parser: p }),
127 );
128
129 let preset_selection_events = args
130 .preset
131 .as_ref()
132 .map(|p| CliEvent::AgentPresetSet {
133 preset: format!("{p:?}"),
134 })
135 .into_iter();
136
137 let config_events = std::iter::empty()
138 .chain(args.no_isolation.then_some(CliEvent::IsolationModeDisabled))
139 .chain(
140 args.review_depth
141 .clone()
142 .map(|d| CliEvent::ReviewDepthSet { depth: d }),
143 )
144 .chain(
145 args.git_user_name
146 .as_ref()
147 .map(|n| CliEvent::GitUserNameSet {
148 name: n.trim().to_string(),
149 }),
150 )
151 .chain(
152 args.git_user_email
153 .as_ref()
154 .map(|e| CliEvent::GitUserEmailSet {
155 email: e.trim().to_string(),
156 }),
157 )
158 .chain(
159 args.show_streaming_metrics
160 .then_some(CliEvent::StreamingMetricsEnabled),
161 );
162
163 verbosity_events
164 .into_iter()
165 .chain(preset_events)
166 .chain(iteration_events)
167 .chain(agent_events)
168 .chain(preset_selection_events)
169 .chain(config_events)
170 .chain(std::iter::once(CliEvent::CliProcessingComplete))
171 .collect()
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use crate::cli::Args;
178 use clap::Parser;
179
180 #[test]
181 fn test_args_to_events_empty() {
182 let args = Args::parse_from(["ralph"]);
183 let events = args_to_events(&args);
184
185 assert!(
187 events.contains(&CliEvent::CliProcessingComplete),
188 "Should always have completion event"
189 );
190
191 assert!(
193 !events.iter().any(|e| e != &CliEvent::CliProcessingComplete),
194 "Should have no other events for empty args"
195 );
196 }
197
198 #[test]
199 fn test_args_to_events_quick_preset() {
200 let args = Args::parse_from(["ralph", "-Q"]);
201 let events = args_to_events(&args);
202
203 assert!(
204 events.contains(&CliEvent::QuickPresetApplied),
205 "Should have quick preset event"
206 );
207 assert!(events.contains(&CliEvent::CliProcessingComplete));
208 }
209
210 #[test]
211 fn test_args_to_events_rapid_preset() {
212 let args = Args::parse_from(["ralph", "-U"]);
213 let events = args_to_events(&args);
214
215 assert!(
216 events.contains(&CliEvent::RapidPresetApplied),
217 "Should have rapid preset event"
218 );
219 assert!(events.contains(&CliEvent::CliProcessingComplete));
220 }
221
222 #[test]
223 fn test_args_to_events_long_preset() {
224 let args = Args::parse_from(["ralph", "-L"]);
225 let events = args_to_events(&args);
226
227 assert!(
228 events.contains(&CliEvent::LongPresetApplied),
229 "Should have long preset event"
230 );
231 assert!(events.contains(&CliEvent::CliProcessingComplete));
232 }
233
234 #[test]
235 fn test_args_to_events_standard_preset() {
236 let args = Args::parse_from(["ralph", "-S"]);
237 let events = args_to_events(&args);
238
239 assert!(
240 events.contains(&CliEvent::StandardPresetApplied),
241 "Should have standard preset event"
242 );
243 assert!(events.contains(&CliEvent::CliProcessingComplete));
244 }
245
246 #[test]
247 fn test_args_to_events_thorough_preset() {
248 let args = Args::parse_from(["ralph", "-T"]);
249 let events = args_to_events(&args);
250
251 assert!(
252 events.contains(&CliEvent::ThoroughPresetApplied),
253 "Should have thorough preset event"
254 );
255 assert!(events.contains(&CliEvent::CliProcessingComplete));
256 }
257
258 #[test]
259 fn test_args_to_events_explicit_iters() {
260 let args = Args::parse_from(["ralph", "-D", "7", "-R", "3"]);
261 let events = args_to_events(&args);
262
263 assert!(
264 events.contains(&CliEvent::DeveloperItersSet { value: 7 }),
265 "Should have developer iters event"
266 );
267 assert!(
268 events.contains(&CliEvent::ReviewerReviewsSet { value: 3 }),
269 "Should have reviewer reviews event"
270 );
271 }
272
273 #[test]
274 fn test_args_to_events_preset_plus_explicit_override() {
275 let args = Args::parse_from(["ralph", "-Q", "-D", "10", "-R", "5"]);
276 let events = args_to_events(&args);
277
278 assert!(events.contains(&CliEvent::QuickPresetApplied));
280 assert!(events.contains(&CliEvent::DeveloperItersSet { value: 10 }));
281 assert!(events.contains(&CliEvent::ReviewerReviewsSet { value: 5 }));
282
283 let preset_idx = events
285 .iter()
286 .position(|e| e == &CliEvent::QuickPresetApplied)
287 .expect("Should have quick preset");
288 let iters_idx = events
289 .iter()
290 .position(|e| e == &CliEvent::DeveloperItersSet { value: 10 })
291 .expect("Should have developer iters");
292
293 assert!(
294 preset_idx < iters_idx,
295 "Preset should come before explicit override"
296 );
297 }
298
299 #[test]
300 fn test_args_to_events_agent_selection() {
301 let args = Args::parse_from(["ralph", "-a", "claude", "-r", "gpt"]);
302 let events = args_to_events(&args);
303
304 assert!(
305 events.contains(&CliEvent::DeveloperAgentSet {
306 agent: "claude".to_string()
307 }),
308 "Should have developer agent event"
309 );
310 assert!(
311 events.contains(&CliEvent::ReviewerAgentSet {
312 agent: "gpt".to_string()
313 }),
314 "Should have reviewer agent event"
315 );
316 }
317
318 #[test]
319 fn test_args_to_events_verbose_mode() {
320 let args = Args::parse_from(["ralph", "-v", "3"]);
321 let events = args_to_events(&args);
322
323 assert!(
324 events.contains(&CliEvent::VerbositySet { level: 3 }),
325 "Should have verbosity set event"
326 );
327 }
328
329 #[test]
330 fn test_args_to_events_debug_mode() {
331 let args = Args::parse_from(["ralph", "--debug"]);
332 let events = args_to_events(&args);
333
334 assert!(
335 events.contains(&CliEvent::DebugModeEnabled),
336 "Should have debug mode event"
337 );
338 }
339
340 #[test]
341 fn test_args_to_events_no_isolation() {
342 let args = Args::parse_from(["ralph", "--no-isolation"]);
343 let events = args_to_events(&args);
344
345 assert!(
346 events.contains(&CliEvent::IsolationModeDisabled),
347 "Should have isolation mode disabled event"
348 );
349 }
350
351 #[test]
352 fn test_args_to_events_git_identity() {
353 let args = Args::parse_from([
354 "ralph",
355 "--git-user-name",
356 "John Doe",
357 "--git-user-email",
358 "john@example.com",
359 ]);
360 let events = args_to_events(&args);
361
362 assert!(
363 events.contains(&CliEvent::GitUserNameSet {
364 name: "John Doe".to_string()
365 }),
366 "Should have git user name event"
367 );
368 assert!(
369 events.contains(&CliEvent::GitUserEmailSet {
370 email: "john@example.com".to_string()
371 }),
372 "Should have git user email event"
373 );
374 }
375
376 #[test]
377 fn test_args_to_events_streaming_metrics() {
378 let args = Args::parse_from(["ralph", "--show-streaming-metrics"]);
379 let events = args_to_events(&args);
380
381 assert!(
382 events.contains(&CliEvent::StreamingMetricsEnabled),
383 "Should have streaming metrics event"
384 );
385 }
386
387 #[test]
388 fn test_args_parses_pause_on_exit_mode() {
389 let args = Args::try_parse_from(["ralph", "--pause-on-exit", "always"])
390 .expect("pause-on-exit should parse");
391
392 assert_eq!(args.pause_on_exit, crate::cli::PauseOnExitMode::Always);
393 }
394}