1use crate::field::{FieldError, FieldResult, FormField, Widget};
2
3pub struct ChoiceField {
5 pub name: String,
6 pub label: Option<String>,
7 pub required: bool,
8 pub help_text: Option<String>,
9 pub widget: Widget,
10 pub initial: Option<serde_json::Value>,
11 pub choices: Vec<(String, String)>, }
13
14impl ChoiceField {
15 pub fn new(name: String, choices: Vec<(String, String)>) -> Self {
27 Self {
28 name,
29 label: None,
30 required: true,
31 help_text: None,
32 widget: Widget::Select {
33 choices: choices.clone(),
34 },
35 initial: None,
36 choices,
37 }
38 }
39}
40
41impl FormField for ChoiceField {
42 fn name(&self) -> &str {
43 &self.name
44 }
45
46 fn label(&self) -> Option<&str> {
47 self.label.as_deref()
48 }
49
50 fn required(&self) -> bool {
51 self.required
52 }
53
54 fn help_text(&self) -> Option<&str> {
55 self.help_text.as_deref()
56 }
57
58 fn widget(&self) -> &Widget {
59 &self.widget
60 }
61
62 fn initial(&self) -> Option<&serde_json::Value> {
63 self.initial.as_ref()
64 }
65
66 fn clean(&self, value: Option<&serde_json::Value>) -> FieldResult<serde_json::Value> {
67 match value {
68 None if self.required => Err(FieldError::required(None)),
69 None => Ok(serde_json::Value::String(String::new())),
70 Some(v) => {
71 let s = v
72 .as_str()
73 .ok_or_else(|| FieldError::Invalid("Expected string".to_string()))?;
74
75 let s = s.trim();
76
77 if s.is_empty() {
78 if self.required {
79 return Err(FieldError::required(None));
80 }
81 return Ok(serde_json::Value::String(String::new()));
82 }
83
84 let valid = self.choices.iter().any(|(value, _)| value == s);
86 if !valid {
87 return Err(FieldError::Validation(format!(
88 "Select a valid choice. '{}' is not one of the available choices",
89 s
90 )));
91 }
92
93 Ok(serde_json::Value::String(s.to_string()))
94 }
95 }
96 }
97}
98
99pub struct MultipleChoiceField {
101 pub name: String,
102 pub label: Option<String>,
103 pub required: bool,
104 pub help_text: Option<String>,
105 pub widget: Widget,
106 pub initial: Option<serde_json::Value>,
107 pub choices: Vec<(String, String)>,
108}
109
110impl MultipleChoiceField {
111 pub fn new(name: String, choices: Vec<(String, String)>) -> Self {
112 Self {
113 name,
114 label: None,
115 required: true,
116 help_text: None,
117 widget: Widget::Select {
118 choices: choices.clone(),
119 },
120 initial: None,
121 choices,
122 }
123 }
124}
125
126impl FormField for MultipleChoiceField {
127 fn name(&self) -> &str {
128 &self.name
129 }
130
131 fn label(&self) -> Option<&str> {
132 self.label.as_deref()
133 }
134
135 fn required(&self) -> bool {
136 self.required
137 }
138
139 fn help_text(&self) -> Option<&str> {
140 self.help_text.as_deref()
141 }
142
143 fn widget(&self) -> &Widget {
144 &self.widget
145 }
146
147 fn initial(&self) -> Option<&serde_json::Value> {
148 self.initial.as_ref()
149 }
150
151 fn clean(&self, value: Option<&serde_json::Value>) -> FieldResult<serde_json::Value> {
152 match value {
153 None if self.required => Err(FieldError::required(None)),
154 None => Ok(serde_json::json!([])),
155 Some(v) => {
156 let values: Vec<String> = if let Some(arr) = v.as_array() {
157 arr.iter()
158 .filter_map(|v| v.as_str().map(|s| s.to_string()))
159 .collect()
160 } else if let Some(s) = v.as_str() {
161 vec![s.to_string()]
162 } else {
163 return Err(FieldError::Invalid("Expected array or string".to_string()));
164 };
165
166 if values.is_empty() && self.required {
167 return Err(FieldError::required(None));
168 }
169
170 for val in &values {
172 let valid = self.choices.iter().any(|(choice, _)| choice == val);
173 if !valid {
174 return Err(FieldError::Validation(format!(
175 "Select a valid choice. '{}' is not one of the available choices",
176 val
177 )));
178 }
179 }
180
181 Ok(serde_json::json!(values))
182 }
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn test_choicefield_valid() {
193 let choices = vec![
194 ("1".to_string(), "One".to_string()),
195 ("2".to_string(), "Two".to_string()),
196 ];
197 let field = ChoiceField::new("number".to_string(), choices);
198
199 assert_eq!(
200 field.clean(Some(&serde_json::json!("1"))).unwrap(),
201 serde_json::json!("1")
202 );
203 }
204
205 #[test]
206 fn test_choicefield_invalid() {
207 let choices = vec![("1".to_string(), "One".to_string())];
208 let field = ChoiceField::new("number".to_string(), choices);
209
210 assert!(matches!(
211 field.clean(Some(&serde_json::json!("3"))),
212 Err(FieldError::Validation(_))
213 ));
214 }
215
216 #[test]
217 fn test_multiplechoicefield() {
218 let choices = vec![
219 ("a".to_string(), "A".to_string()),
220 ("b".to_string(), "B".to_string()),
221 ];
222 let field = MultipleChoiceField::new("letters".to_string(), choices);
223
224 assert_eq!(
225 field.clean(Some(&serde_json::json!(["a", "b"]))).unwrap(),
226 serde_json::json!(["a", "b"])
227 );
228
229 assert!(matches!(
230 field.clean(Some(&serde_json::json!(["a", "c"]))),
231 Err(FieldError::Validation(_))
232 ));
233 }
234
235 #[test]
236 fn test_choicefield_required() {
237 let choices = vec![("1".to_string(), "One".to_string())];
238 let field = ChoiceField::new("number".to_string(), choices);
239
240 assert!(field.clean(None).is_err());
242
243 assert!(field.clean(Some(&serde_json::json!(""))).is_err());
245 }
246
247 #[test]
248 fn test_choicefield_not_required() {
249 let choices = vec![("1".to_string(), "One".to_string())];
250 let mut field = ChoiceField::new("number".to_string(), choices);
251 field.required = false;
252
253 assert_eq!(field.clean(None).unwrap(), serde_json::json!(""));
255
256 assert_eq!(
258 field.clean(Some(&serde_json::json!(""))).unwrap(),
259 serde_json::json!("")
260 );
261 }
262
263 #[test]
264 fn test_choicefield_whitespace_trimming() {
265 let choices = vec![("1".to_string(), "One".to_string())];
266 let field = ChoiceField::new("number".to_string(), choices);
267
268 assert_eq!(
270 field.clean(Some(&serde_json::json!(" 1 "))).unwrap(),
271 serde_json::json!("1")
272 );
273 }
274
275 #[test]
276 fn test_choicefield_multiple_choices() {
277 let choices = vec![
278 ("a".to_string(), "Alpha".to_string()),
279 ("b".to_string(), "Beta".to_string()),
280 ("c".to_string(), "Gamma".to_string()),
281 ];
282 let field = ChoiceField::new("greek".to_string(), choices);
283
284 assert!(field.clean(Some(&serde_json::json!("a"))).is_ok());
286 assert!(field.clean(Some(&serde_json::json!("b"))).is_ok());
287 assert!(field.clean(Some(&serde_json::json!("c"))).is_ok());
288
289 assert!(field.clean(Some(&serde_json::json!("d"))).is_err());
291 }
292
293 #[test]
294 fn test_choicefield_widget_type() {
295 let choices = vec![("1".to_string(), "One".to_string())];
296 let field = ChoiceField::new("number".to_string(), choices.clone());
297
298 match field.widget() {
300 Widget::Select {
301 choices: widget_choices,
302 } => {
303 assert_eq!(widget_choices, &choices);
304 }
305 _ => panic!("Expected Select widget"),
306 }
307 }
308
309 #[test]
310 fn test_choicefield_empty_choices() {
311 let choices: Vec<(String, String)> = vec![];
312 let field = ChoiceField::new("empty".to_string(), choices);
313
314 assert!(matches!(
316 field.clean(Some(&serde_json::json!("anything"))),
317 Err(FieldError::Validation(_))
318 ));
319 }
320
321 #[test]
322 fn test_choicefield_case_sensitive() {
323 let choices = vec![("abc".to_string(), "ABC".to_string())];
324 let field = ChoiceField::new("text".to_string(), choices);
325
326 assert!(field.clean(Some(&serde_json::json!("abc"))).is_ok());
328
329 assert!(matches!(
331 field.clean(Some(&serde_json::json!("ABC"))),
332 Err(FieldError::Validation(_))
333 ));
334 }
335
336 #[test]
337 fn test_multiplechoicefield_required() {
338 let choices = vec![("1".to_string(), "One".to_string())];
339 let field = MultipleChoiceField::new("numbers".to_string(), choices);
340
341 assert!(field.clean(None).is_err());
343
344 assert!(field.clean(Some(&serde_json::json!([]))).is_err());
346 }
347
348 #[test]
349 fn test_multiplechoicefield_not_required() {
350 let choices = vec![("1".to_string(), "One".to_string())];
351 let mut field = MultipleChoiceField::new("numbers".to_string(), choices);
352 field.required = false;
353
354 assert_eq!(field.clean(None).unwrap(), serde_json::json!([]));
356
357 assert_eq!(
359 field.clean(Some(&serde_json::json!([]))).unwrap(),
360 serde_json::json!([])
361 );
362 }
363
364 #[test]
365 fn test_multiplechoicefield_single_value() {
366 let choices = vec![
367 ("a".to_string(), "A".to_string()),
368 ("b".to_string(), "B".to_string()),
369 ];
370 let field = MultipleChoiceField::new("letters".to_string(), choices);
371
372 assert_eq!(
374 field.clean(Some(&serde_json::json!("a"))).unwrap(),
375 serde_json::json!(["a"])
376 );
377
378 assert!(matches!(
380 field.clean(Some(&serde_json::json!("z"))),
381 Err(FieldError::Validation(_))
382 ));
383 }
384
385 #[test]
386 fn test_multiplechoicefield_multiple_values() {
387 let choices = vec![
388 ("1".to_string(), "One".to_string()),
389 ("2".to_string(), "Two".to_string()),
390 ("3".to_string(), "Three".to_string()),
391 ];
392 let field = MultipleChoiceField::new("numbers".to_string(), choices);
393
394 assert_eq!(
396 field.clean(Some(&serde_json::json!(["1", "2"]))).unwrap(),
397 serde_json::json!(["1", "2"])
398 );
399
400 assert_eq!(
401 field
402 .clean(Some(&serde_json::json!(["1", "2", "3"])))
403 .unwrap(),
404 serde_json::json!(["1", "2", "3"])
405 );
406
407 assert!(matches!(
409 field.clean(Some(&serde_json::json!(["1", "2", "4"]))),
410 Err(FieldError::Validation(_))
411 ));
412 }
413
414 #[test]
415 fn test_multiplechoicefield_duplicate_values() {
416 let choices = vec![
417 ("a".to_string(), "A".to_string()),
418 ("b".to_string(), "B".to_string()),
419 ];
420 let field = MultipleChoiceField::new("letters".to_string(), choices);
421
422 let result = field
424 .clean(Some(&serde_json::json!(["a", "a", "b"])))
425 .unwrap();
426 assert_eq!(result, serde_json::json!(["a", "a", "b"]));
427 }
428
429 #[test]
430 fn test_multiplechoicefield_widget_type() {
431 let choices = vec![("1".to_string(), "One".to_string())];
432 let field = MultipleChoiceField::new("numbers".to_string(), choices.clone());
433
434 match field.widget() {
436 Widget::Select {
437 choices: widget_choices,
438 } => {
439 assert_eq!(widget_choices, &choices);
440 }
441 _ => panic!("Expected Select widget"),
442 }
443 }
444}