dampen_cli/commands/check/
cross_widget.rs1use crate::commands::check::errors::CheckError;
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6#[derive(Debug, Clone)]
7pub struct RadioButton {
8 pub value: String,
9 pub file: PathBuf,
10 pub line: u32,
11 pub col: u32,
12 pub handler: Option<String>,
13}
14
15#[derive(Debug)]
16pub struct RadioGroup {
17 pub id: String,
18 pub buttons: Vec<RadioButton>,
19}
20
21impl RadioGroup {
22 pub fn new(id: String) -> Self {
23 Self {
24 id,
25 buttons: Vec::new(),
26 }
27 }
28
29 pub fn add_button(&mut self, button: RadioButton) {
30 self.buttons.push(button);
31 }
32
33 pub fn validate(&self) -> Vec<CheckError> {
35 let mut errors = Vec::new();
36
37 let mut seen_values: HashMap<String, &RadioButton> = HashMap::new();
39 for button in &self.buttons {
40 if let Some(first) = seen_values.get(&button.value) {
41 errors.push(CheckError::DuplicateRadioValue {
43 value: button.value.clone(),
44 group: self.id.clone(),
45 file: button.file.clone(),
46 line: button.line,
47 col: button.col,
48 first_file: first.file.clone(),
49 first_line: first.line,
50 first_col: first.col,
51 });
52 } else {
53 seen_values.insert(button.value.clone(), button);
54 }
55 }
56
57 if self.buttons.len() > 1 {
59 let first_handler = &self.buttons[0].handler;
60 let mut handler_list = Vec::new();
61 let mut has_inconsistency = false;
62
63 for button in &self.buttons {
64 if let Some(ref h) = button.handler
65 && !handler_list.contains(h)
66 {
67 handler_list.push(h.clone());
68 }
69 if &button.handler != first_handler {
70 has_inconsistency = true;
71 }
72 }
73
74 if has_inconsistency {
75 let button = &self.buttons[1];
77 errors.push(CheckError::InconsistentRadioHandlers {
78 group: self.id.clone(),
79 file: button.file.clone(),
80 line: button.line,
81 col: button.col,
82 handlers: handler_list.join(", "),
83 });
84 }
85 }
86
87 errors
88 }
89}
90
91#[derive(Debug, Default)]
92pub struct RadioGroupValidator {
93 groups: HashMap<String, RadioGroup>,
94}
95
96impl RadioGroupValidator {
97 pub fn new() -> Self {
98 Self::default()
99 }
100
101 pub fn add_radio(
103 &mut self,
104 group_id: &str,
105 value: &str,
106 file: &str,
107 line: u32,
108 col: u32,
109 handler: Option<String>,
110 ) {
111 let button = RadioButton {
112 value: value.to_string(),
113 file: PathBuf::from(file),
114 line,
115 col,
116 handler,
117 };
118
119 self.groups
120 .entry(group_id.to_string())
121 .or_insert_with(|| RadioGroup::new(group_id.to_string()))
122 .add_button(button);
123 }
124
125 pub fn validate(&self) -> Vec<CheckError> {
127 let mut errors = Vec::new();
128 for group in self.groups.values() {
129 errors.extend(group.validate());
130 }
131 errors
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_radio_group_with_unique_values() {
141 let mut group = RadioGroup::new("test".to_string());
142 group.add_button(RadioButton {
143 value: "opt1".to_string(),
144 file: PathBuf::from("test.dampen"),
145 line: 10,
146 col: 5,
147 handler: Some("handler".to_string()),
148 });
149 group.add_button(RadioButton {
150 value: "opt2".to_string(),
151 file: PathBuf::from("test.dampen"),
152 line: 15,
153 col: 5,
154 handler: Some("handler".to_string()),
155 });
156
157 let errors = group.validate();
158 assert_eq!(errors.len(), 0);
159 }
160
161 #[test]
162 fn test_radio_group_with_duplicate_values() {
163 let mut group = RadioGroup::new("test".to_string());
164 group.add_button(RadioButton {
165 value: "opt1".to_string(),
166 file: PathBuf::from("test.dampen"),
167 line: 10,
168 col: 5,
169 handler: Some("handler".to_string()),
170 });
171 group.add_button(RadioButton {
172 value: "opt1".to_string(),
173 file: PathBuf::from("test.dampen"),
174 line: 15,
175 col: 5,
176 handler: Some("handler".to_string()),
177 });
178
179 let errors = group.validate();
180 assert_eq!(errors.len(), 1);
181 match &errors[0] {
182 CheckError::DuplicateRadioValue { value, .. } => {
183 assert_eq!(value, "opt1");
184 }
185 _ => panic!("Expected DuplicateRadioValue error"),
186 }
187 }
188}