1use crate::Widget;
4use crate::field::{FieldError, FieldResult, FormField};
5use crate::model_form::FormModel;
6use serde_json::Value;
7use std::collections::HashMap;
8use std::marker::PhantomData;
9
10fn choice_value_text(value: &Value) -> String {
11 match value {
12 Value::String(value) => format!("id:{value}"),
13 Value::Number(value) => format!("id:{value}"),
14 Value::Bool(value) => format!("bool:{value}"),
15 Value::Null => "null:".to_string(),
16 Value::Array(value) => format!("array:{}", Value::Array(value.clone())),
17 Value::Object(value) => format!("object:{}", Value::Object(value.clone())),
18 }
19}
20
21pub struct ModelChoiceField<T: FormModel> {
25 pub name: String,
27 pub required: bool,
29 pub error_messages: HashMap<String, String>,
31 pub widget: Widget,
33 pub help_text: String,
35 pub initial: Option<Value>,
37 pub queryset: Vec<T>,
39 pub empty_label: Option<String>,
41 _phantom: PhantomData<T>,
42}
43
44impl<T: FormModel> ModelChoiceField<T> {
45 pub fn new(name: impl Into<String>, queryset: Vec<T>) -> Self {
95 let mut error_messages = HashMap::new();
96 error_messages.insert(
97 "required".to_string(),
98 "This field is required.".to_string(),
99 );
100 error_messages.insert(
101 "invalid_choice".to_string(),
102 "Select a valid choice.".to_string(),
103 );
104
105 Self {
106 name: name.into(),
107 required: true,
108 error_messages,
109 widget: Widget::Select {
110 choices: Vec::new(),
111 },
112 help_text: String::new(),
113 initial: None,
114 queryset,
115 empty_label: Some("--------".to_string()),
116 _phantom: PhantomData,
117 }
118 }
119 pub fn required(mut self, required: bool) -> Self {
121 self.required = required;
122 self
123 }
124 pub fn help_text(mut self, text: impl Into<String>) -> Self {
126 self.help_text = text.into();
127 self
128 }
129 pub fn initial(mut self, value: Value) -> Self {
131 self.initial = Some(value);
132 self
133 }
134 pub fn empty_label(mut self, label: Option<String>) -> Self {
136 self.empty_label = label;
137 self
138 }
139 pub fn error_message(
141 mut self,
142 error_type: impl Into<String>,
143 message: impl Into<String>,
144 ) -> Self {
145 self.error_messages
146 .insert(error_type.into(), message.into());
147 self
148 }
149
150 #[allow(dead_code)]
154 fn get_choices(&self) -> Vec<(String, String)> {
155 let mut choices = Vec::new();
156
157 if !self.required && self.empty_label.is_some() {
158 choices.push(("".to_string(), self.empty_label.clone().unwrap()));
159 }
160
161 for instance in &self.queryset {
163 let value = instance.to_choice_value();
164 let label = instance.to_choice_label();
165 choices.push((value, label));
166 }
167
168 choices
169 }
170}
171
172impl<T: FormModel> FormField for ModelChoiceField<T> {
173 fn name(&self) -> &str {
174 &self.name
175 }
176
177 fn label(&self) -> Option<&str> {
178 None
179 }
180
181 fn widget(&self) -> &Widget {
182 &self.widget
183 }
184
185 fn required(&self) -> bool {
186 self.required
187 }
188
189 fn initial(&self) -> Option<&Value> {
190 self.initial.as_ref()
191 }
192
193 fn help_text(&self) -> Option<&str> {
194 if self.help_text.is_empty() {
195 None
196 } else {
197 Some(&self.help_text)
198 }
199 }
200
201 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
202 if value.is_none() || value == Some(&Value::Null) {
203 if self.required {
204 let error_msg = self
205 .error_messages
206 .get("required")
207 .cloned()
208 .unwrap_or_else(|| "This field is required.".to_string());
209 return Err(FieldError::validation(None, &error_msg));
210 }
211 return Ok(Value::Null);
212 }
213
214 let s = match value.unwrap() {
215 Value::String(s) => s.as_str(),
216 Value::Number(n) => {
217 &n.to_string()
219 }
220 _ => {
221 let error_msg = self
222 .error_messages
223 .get("invalid_choice")
224 .cloned()
225 .unwrap_or_else(|| "Select a valid choice.".to_string());
226 return Err(FieldError::validation(None, &error_msg));
227 }
228 };
229
230 if s.is_empty() {
231 if self.required {
232 let error_msg = self
233 .error_messages
234 .get("required")
235 .cloned()
236 .unwrap_or_else(|| "This field is required.".to_string());
237 return Err(FieldError::validation(None, &error_msg));
238 }
239 return Ok(Value::Null);
240 }
241
242 let choice_exists = self
244 .queryset
245 .iter()
246 .any(|instance| instance.to_choice_value() == s);
247
248 if !choice_exists {
249 let error_msg = self
250 .error_messages
251 .get("invalid_choice")
252 .cloned()
253 .unwrap_or_else(|| "Select a valid choice.".to_string());
254 return Err(FieldError::validation(None, &error_msg));
255 }
256
257 Ok(Value::String(s.to_string()))
258 }
259
260 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
261 match (initial, data) {
262 (None, None) => false,
263 (Some(_), None) | (None, Some(_)) => true,
264 (Some(a), Some(b)) => a != b,
265 }
266 }
267}
268
269pub struct ModelMultipleChoiceField<T: FormModel> {
277 pub name: String,
279 pub required: bool,
281 pub error_messages: HashMap<String, String>,
283 pub widget: Widget,
285 pub help_text: String,
287 pub initial: Option<Value>,
289 pub queryset: Vec<T>,
291 _phantom: PhantomData<T>,
292}
293
294impl<T: FormModel> ModelMultipleChoiceField<T> {
295 pub fn new(name: impl Into<String>, queryset: Vec<T>) -> Self {
350 let mut error_messages = HashMap::new();
351 error_messages.insert(
352 "required".to_string(),
353 "This field is required.".to_string(),
354 );
355 error_messages.insert(
356 "invalid_choice".to_string(),
357 "Select a valid choice.".to_string(),
358 );
359 error_messages.insert(
360 "invalid_list".to_string(),
361 "Enter a list of values.".to_string(),
362 );
363
364 Self {
365 name: name.into(),
366 required: true,
367 error_messages,
368 widget: Widget::Select {
369 choices: Vec::new(),
370 },
371 help_text: String::new(),
372 initial: None,
373 queryset,
374 _phantom: PhantomData,
375 }
376 }
377 pub fn required(mut self, required: bool) -> Self {
379 self.required = required;
380 self
381 }
382 pub fn help_text(mut self, text: impl Into<String>) -> Self {
384 self.help_text = text.into();
385 self
386 }
387 pub fn initial(mut self, value: Value) -> Self {
389 self.initial = Some(value);
390 self
391 }
392 pub fn error_message(
394 mut self,
395 error_type: impl Into<String>,
396 message: impl Into<String>,
397 ) -> Self {
398 self.error_messages
399 .insert(error_type.into(), message.into());
400 self
401 }
402
403 #[allow(dead_code)]
406 fn get_choices(&self) -> Vec<(String, String)> {
407 let mut choices = Vec::new();
408
409 for instance in &self.queryset {
411 let value = instance.to_choice_value();
412 let label = instance.to_choice_label();
413 choices.push((value, label));
414 }
415
416 choices
417 }
418}
419
420impl<T: FormModel> FormField for ModelMultipleChoiceField<T> {
421 fn name(&self) -> &str {
422 &self.name
423 }
424
425 fn label(&self) -> Option<&str> {
426 None
427 }
428
429 fn widget(&self) -> &Widget {
430 &self.widget
431 }
432
433 fn required(&self) -> bool {
434 self.required
435 }
436
437 fn initial(&self) -> Option<&Value> {
438 self.initial.as_ref()
439 }
440
441 fn help_text(&self) -> Option<&str> {
442 if self.help_text.is_empty() {
443 None
444 } else {
445 Some(&self.help_text)
446 }
447 }
448
449 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
450 if value.is_none() || value == Some(&Value::Null) {
451 if self.required {
452 let error_msg = self
453 .error_messages
454 .get("required")
455 .cloned()
456 .unwrap_or_else(|| "This field is required.".to_string());
457 return Err(FieldError::validation(None, &error_msg));
458 }
459 return Ok(Value::Array(Vec::new()));
460 }
461
462 let values = match value.unwrap() {
463 Value::Array(arr) => arr.clone(),
464 Value::String(s) if s.is_empty() => {
465 if self.required {
466 let error_msg = self
467 .error_messages
468 .get("required")
469 .cloned()
470 .unwrap_or_else(|| "This field is required.".to_string());
471 return Err(FieldError::validation(None, &error_msg));
472 }
473 return Ok(Value::Array(Vec::new()));
474 }
475 Value::String(s) => {
476 s.split(',')
478 .map(|v| Value::String(v.trim().to_string()))
479 .collect()
480 }
481 _ => {
482 let error_msg = self
483 .error_messages
484 .get("invalid_list")
485 .cloned()
486 .unwrap_or_else(|| "Enter a list of values.".to_string());
487 return Err(FieldError::validation(None, &error_msg));
488 }
489 };
490
491 if values.is_empty() && self.required {
492 let error_msg = self
493 .error_messages
494 .get("required")
495 .cloned()
496 .unwrap_or_else(|| "This field is required.".to_string());
497 return Err(FieldError::validation(None, &error_msg));
498 }
499
500 for value in &values {
502 if let Some(value_str) = value.as_str() {
503 let choice_exists = self
504 .queryset
505 .iter()
506 .any(|instance| instance.to_choice_value() == value_str);
507
508 if !choice_exists {
509 let error_msg = self
510 .error_messages
511 .get("invalid_choice")
512 .cloned()
513 .unwrap_or_else(|| format!("'{}' is not a valid choice.", value_str));
514 return Err(FieldError::validation(None, &error_msg));
515 }
516 }
517 }
518
519 Ok(Value::Array(values))
520 }
521
522 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
523 match (initial, data) {
524 (None, None) => false,
525 (Some(_), None) | (None, Some(_)) => true,
526 (Some(Value::Array(a)), Some(Value::Array(b))) => {
527 if a.len() != b.len() {
528 return true;
529 }
530
531 let mut initial_values: Vec<_> = a.iter().map(choice_value_text).collect();
532 let mut submitted_values: Vec<_> = b.iter().map(choice_value_text).collect();
533 initial_values.sort_unstable();
534 submitted_values.sort_unstable();
535 initial_values != submitted_values
536 }
537 (Some(a), Some(b)) => a != b,
538 }
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545 use crate::FormField;
546 use rstest::rstest;
547 use serde_json::json;
548
549 struct TestModel {
551 id: i32,
552 name: String,
553 }
554
555 impl FormModel for TestModel {
556 fn field_names() -> Vec<String> {
557 vec!["id".to_string(), "name".to_string()]
558 }
559
560 fn get_field(&self, name: &str) -> Option<Value> {
561 match name {
562 "id" => Some(Value::Number(self.id.into())),
563 "name" => Some(Value::String(self.name.clone())),
564 _ => None,
565 }
566 }
567
568 fn set_field(&mut self, _name: &str, _value: Value) -> Result<(), String> {
569 Ok(())
570 }
571
572 fn save(&mut self) -> Result<(), String> {
573 Ok(())
574 }
575 }
576
577 struct StringKeyModel {
578 id: String,
579 name: String,
580 }
581
582 impl FormModel for StringKeyModel {
583 fn field_names() -> Vec<String> {
584 vec!["id".to_string(), "name".to_string()]
585 }
586
587 fn get_field(&self, name: &str) -> Option<Value> {
588 match name {
589 "id" => Some(Value::String(self.id.clone())),
590 "name" => Some(Value::String(self.name.clone())),
591 _ => None,
592 }
593 }
594
595 fn set_field(&mut self, _name: &str, _value: Value) -> Result<(), String> {
596 Ok(())
597 }
598
599 fn save(&mut self) -> Result<(), String> {
600 Ok(())
601 }
602 }
603
604 #[test]
605 fn test_model_choice_field_basic() {
606 let queryset = vec![
607 TestModel {
608 id: 1,
609 name: "Option 1".to_string(),
610 },
611 TestModel {
612 id: 2,
613 name: "Option 2".to_string(),
614 },
615 ];
616
617 let field = ModelChoiceField::new("choice", queryset);
618
619 assert_eq!(field.name(), "choice");
620 assert!(FormField::required(&field));
621 }
622
623 #[test]
624 fn test_model_choice_field_required() {
625 let field = ModelChoiceField::new("choice", Vec::<TestModel>::new());
626
627 let result = field.clean(None);
628 assert!(result.is_err());
629 }
630
631 #[test]
632 fn test_model_choice_field_not_required() {
633 let field = ModelChoiceField::new("choice", Vec::<TestModel>::new()).required(false);
634
635 let result = field.clean(None);
636 assert!(result.is_ok());
637 assert_eq!(result.unwrap(), Value::Null);
638 }
639
640 #[test]
641 fn test_model_multiple_choice_field_basic() {
642 let queryset = vec![
643 TestModel {
644 id: 1,
645 name: "Option 1".to_string(),
646 },
647 TestModel {
648 id: 2,
649 name: "Option 2".to_string(),
650 },
651 ];
652
653 let field = ModelMultipleChoiceField::new("choices", queryset);
654
655 assert_eq!(field.name(), "choices");
656 assert!(FormField::required(&field));
657 }
658
659 #[test]
660 fn test_model_multiple_choice_field_array() {
661 let queryset = vec![
662 TestModel {
663 id: 1,
664 name: "Option 1".to_string(),
665 },
666 TestModel {
667 id: 2,
668 name: "Option 2".to_string(),
669 },
670 TestModel {
671 id: 3,
672 name: "Option 3".to_string(),
673 },
674 ];
675
676 let field = ModelMultipleChoiceField::new("choices", queryset).required(false);
677
678 let result = field.clean(Some(&json!(["1", "2"])));
679 assert!(result.is_ok());
680
681 if let Value::Array(arr) = result.unwrap() {
682 assert_eq!(arr.len(), 2);
683 } else {
684 panic!("Expected array");
685 }
686 }
687
688 #[test]
689 fn test_model_multiple_choice_field_comma_separated() {
690 let queryset = vec![
691 TestModel {
692 id: 1,
693 name: "Option 1".to_string(),
694 },
695 TestModel {
696 id: 2,
697 name: "Option 2".to_string(),
698 },
699 TestModel {
700 id: 3,
701 name: "Option 3".to_string(),
702 },
703 ];
704
705 let field = ModelMultipleChoiceField::new("choices", queryset).required(false);
706
707 let result = field.clean(Some(&json!("1,2,3")));
708 assert!(result.is_ok());
709
710 if let Value::Array(arr) = result.unwrap() {
711 assert_eq!(arr.len(), 3);
712 } else {
713 panic!("Expected array");
714 }
715 }
716
717 #[rstest]
718 fn model_choice_field_validates_supported_input_shapes() {
719 let single = ModelChoiceField::new(
721 "choice",
722 vec![
723 TestModel {
724 id: 1,
725 name: "One".to_string(),
726 },
727 TestModel {
728 id: 2,
729 name: "Two".to_string(),
730 },
731 ],
732 )
733 .error_message("invalid_choice", "Unknown choice.");
734 assert_eq!(single.clean(Some(&json!("1"))).unwrap(), json!("1"));
736 assert_eq!(single.clean(Some(&json!(2))).unwrap(), json!("2"));
737 assert_eq!(
738 single.clean(Some(&json!("99"))).unwrap_err().to_string(),
739 "Unknown choice.",
740 );
741 assert_eq!(
742 single.clean(Some(&json!(["1"]))).unwrap_err().to_string(),
743 "Unknown choice.",
744 );
745 assert_eq!(
746 single.clean(None).unwrap_err().to_string(),
747 "This field is required.",
748 );
749 assert_eq!(
750 ModelChoiceField::new("choice", Vec::<TestModel>::new())
751 .required(false)
752 .clean(Some(&json!("")))
753 .unwrap(),
754 Value::Null,
755 );
756
757 let string_single = ModelChoiceField::new(
758 "choice",
759 vec![StringKeyModel {
760 id: "alpha-01".to_string(),
761 name: "Alpha".to_string(),
762 }],
763 );
764 assert_eq!(
765 string_single.clean(Some(&json!("alpha-01"))).unwrap(),
766 json!("alpha-01"),
767 );
768 }
769
770 #[rstest]
771 fn model_multiple_choice_field_validates_supported_input_shapes() {
772 let multiple = ModelMultipleChoiceField::new(
774 "choices",
775 vec![
776 TestModel {
777 id: 1,
778 name: "One".to_string(),
779 },
780 TestModel {
781 id: 2,
782 name: "Two".to_string(),
783 },
784 ],
785 )
786 .error_message("invalid_choice", "Unknown choice.")
787 .error_message("invalid_list", "Values must be a list.");
788
789 assert_eq!(
791 multiple.clean(Some(&json!("1, 2"))).unwrap(),
792 json!(["1", "2"])
793 );
794 assert_eq!(
795 multiple.clean(Some(&json!(["2", "1"]))).unwrap(),
796 json!(["2", "1"])
797 );
798 assert_eq!(
799 multiple
800 .clean(Some(&json!(["1", "99"])))
801 .unwrap_err()
802 .to_string(),
803 "Unknown choice.",
804 );
805 assert_eq!(
806 multiple.clean(Some(&json!(2))).unwrap_err().to_string(),
807 "Values must be a list.",
808 );
809 assert_eq!(
810 multiple.clean(None).unwrap_err().to_string(),
811 "This field is required.",
812 );
813 assert_eq!(
814 ModelMultipleChoiceField::new("choices", Vec::<TestModel>::new())
815 .required(false)
816 .clean(Some(&json!("")))
817 .unwrap(),
818 json!([]),
819 );
820 assert!(!multiple.has_changed(Some(&json!(["1", "2"])), Some(&json!(["1", "2"]))));
821 assert!(!multiple.has_changed(Some(&json!(["1", "2"])), Some(&json!(["2", "1"]))));
822 assert!(!multiple.has_changed(Some(&json!([1, 2])), Some(&json!(["1", "2"]))));
823 assert!(multiple.has_changed(Some(&json!(["true"])), Some(&json!([true]))));
824 assert!(multiple.has_changed(Some(&json!(["null"])), Some(&json!([null]))));
825 assert!(multiple.has_changed(Some(&json!(["1", "1"])), Some(&json!(["1"]))));
826
827 let string_multiple = ModelMultipleChoiceField::new(
828 "choices",
829 vec![
830 StringKeyModel {
831 id: "alpha-01".to_string(),
832 name: "Alpha".to_string(),
833 },
834 StringKeyModel {
835 id: "beta-02".to_string(),
836 name: "Beta".to_string(),
837 },
838 ],
839 );
840 assert_eq!(
841 string_multiple
842 .clean(Some(&json!("beta-02, alpha-01")))
843 .unwrap(),
844 json!(["beta-02", "alpha-01"]),
845 );
846 }
847}