ralph_workflow/cli/reducer/
state.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub enum PresetType {
14 Quick,
16 Rapid,
18 Long,
20 Standard,
22 Thorough,
24}
25
26impl PresetType {
27 #[must_use]
29 pub const fn iteration_counts(self) -> (u32, u32) {
30 match self {
31 Self::Quick => (1, 1),
32 Self::Rapid => (2, 1),
33 Self::Long => (15, 10),
34 Self::Standard => (5, 2),
35 Self::Thorough => (10, 5),
36 }
37 }
38
39 #[must_use]
41 pub const fn developer_iters(self) -> u32 {
42 self.iteration_counts().0
43 }
44
45 #[must_use]
47 pub const fn reviewer_reviews(self) -> u32 {
48 self.iteration_counts().1
49 }
50}
51
52#[derive(Clone, Debug, Default, Serialize, Deserialize)]
58pub struct CliState {
59 pub verbosity: Option<u8>,
62 pub quiet_mode: bool,
64 pub full_mode: bool,
66 pub debug_mode: bool,
68
69 pub preset_applied: Option<PresetType>,
72
73 pub developer_iters: Option<u32>,
76 pub reviewer_reviews: Option<u32>,
78
79 pub developer_agent: Option<String>,
82 pub reviewer_agent: Option<String>,
84 pub developer_model: Option<String>,
86 pub reviewer_model: Option<String>,
88 pub developer_provider: Option<String>,
90 pub reviewer_provider: Option<String>,
92 pub reviewer_json_parser: Option<String>,
94
95 pub isolation_mode: Option<bool>,
98 pub review_depth: Option<String>,
100 pub git_user_name: Option<String>,
102 pub git_user_email: Option<String>,
104 pub streaming_metrics: bool,
106
107 pub agent_preset: Option<String>,
110
111 pub complete: bool,
114}
115
116impl CliState {
117 #[must_use]
119 pub fn initial() -> Self {
120 Self::default()
121 }
122
123 #[must_use]
130 pub fn resolved_developer_iters(&self, config_default: u32) -> u32 {
131 self.developer_iters.unwrap_or_else(|| {
132 self.preset_applied
133 .map_or(config_default, PresetType::developer_iters)
134 })
135 }
136
137 #[must_use]
144 pub fn resolved_reviewer_reviews(&self, config_default: u32) -> u32 {
145 self.reviewer_reviews.unwrap_or_else(|| {
146 self.preset_applied
147 .map_or(config_default, PresetType::reviewer_reviews)
148 })
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_preset_iteration_counts() {
158 assert_eq!(PresetType::Quick.iteration_counts(), (1, 1));
159 assert_eq!(PresetType::Rapid.iteration_counts(), (2, 1));
160 assert_eq!(PresetType::Long.iteration_counts(), (15, 10));
161 assert_eq!(PresetType::Standard.iteration_counts(), (5, 2));
162 assert_eq!(PresetType::Thorough.iteration_counts(), (10, 5));
163 }
164
165 #[test]
166 fn test_preset_individual_accessors() {
167 assert_eq!(PresetType::Quick.developer_iters(), 1);
168 assert_eq!(PresetType::Quick.reviewer_reviews(), 1);
169 assert_eq!(PresetType::Long.developer_iters(), 15);
170 assert_eq!(PresetType::Long.reviewer_reviews(), 10);
171 }
172
173 #[test]
174 fn test_initial_state() {
175 let state = CliState::initial();
176 assert!(!state.complete);
177 assert!(state.preset_applied.is_none());
178 assert!(state.developer_iters.is_none());
179 assert!(state.reviewer_reviews.is_none());
180 assert!(!state.quiet_mode);
181 assert!(!state.streaming_metrics);
182 }
183
184 #[test]
185 fn test_resolved_iters_explicit_override() {
186 let state = CliState {
187 preset_applied: Some(PresetType::Quick),
188 developer_iters: Some(10),
189 reviewer_reviews: Some(5),
190 ..CliState::initial()
191 };
192
193 assert_eq!(state.resolved_developer_iters(99), 10);
195 assert_eq!(state.resolved_reviewer_reviews(99), 5);
196 }
197
198 #[test]
199 fn test_resolved_iters_preset_fallback() {
200 let state = CliState {
201 preset_applied: Some(PresetType::Long),
202 ..CliState::initial()
203 };
204
205 assert_eq!(state.resolved_developer_iters(99), 15);
207 assert_eq!(state.resolved_reviewer_reviews(99), 10);
208 }
209
210 #[test]
211 fn test_resolved_iters_config_fallback() {
212 let state = CliState::initial();
213
214 assert_eq!(state.resolved_developer_iters(5), 5);
216 assert_eq!(state.resolved_reviewer_reviews(2), 2);
217 }
218
219 #[test]
220 fn test_state_serialization() {
221 let state = CliState {
222 preset_applied: Some(PresetType::Thorough),
223 developer_agent: Some("claude".to_string()),
224 ..CliState::initial()
225 };
226
227 let json = serde_json::to_string(&state).unwrap();
228 let deserialized: CliState = serde_json::from_str(&json).unwrap();
229
230 assert_eq!(state.preset_applied, deserialized.preset_applied);
231 assert_eq!(state.developer_agent, deserialized.developer_agent);
232 }
233}