1use crate::Widget;
4use crate::field::{FieldError, FieldResult, FormField};
5use serde_json::Value;
6use std::collections::HashMap;
7
8#[derive(Debug, Clone)]
31pub struct UUIDField {
32 pub name: String,
33 pub required: bool,
34 pub error_messages: HashMap<String, String>,
35 pub widget: Widget,
36 pub help_text: String,
37 pub initial: Option<Value>,
38}
39
40impl UUIDField {
41 pub fn new(name: impl Into<String>) -> Self {
43 let mut error_messages = HashMap::new();
44 error_messages.insert(
45 "required".to_string(),
46 "This field is required.".to_string(),
47 );
48 error_messages.insert("invalid".to_string(), "Enter a valid UUID.".to_string());
49
50 Self {
51 name: name.into(),
52 required: true,
53 error_messages,
54 widget: Widget::TextInput,
55 help_text: String::new(),
56 initial: None,
57 }
58 }
59
60 pub fn required(mut self, required: bool) -> Self {
62 self.required = required;
63 self
64 }
65
66 pub fn help_text(mut self, text: impl Into<String>) -> Self {
68 self.help_text = text.into();
69 self
70 }
71
72 pub fn initial(mut self, value: Value) -> Self {
74 self.initial = Some(value);
75 self
76 }
77
78 pub fn error_message(
80 mut self,
81 error_type: impl Into<String>,
82 message: impl Into<String>,
83 ) -> Self {
84 self.error_messages
85 .insert(error_type.into(), message.into());
86 self
87 }
88
89 fn validate_uuid(&self, s: &str) -> bool {
91 let parts: Vec<&str> = s.split('-').collect();
93 if parts.len() != 5 {
94 return false;
95 }
96
97 if parts[0].len() != 8
98 || parts[1].len() != 4
99 || parts[2].len() != 4
100 || parts[3].len() != 4
101 || parts[4].len() != 12
102 {
103 return false;
104 }
105
106 parts
107 .iter()
108 .all(|part| part.chars().all(|c| c.is_ascii_hexdigit()))
109 }
110}
111
112impl FormField for UUIDField {
113 fn name(&self) -> &str {
114 &self.name
115 }
116
117 fn label(&self) -> Option<&str> {
118 None
119 }
120
121 fn widget(&self) -> &Widget {
122 &self.widget
123 }
124
125 fn required(&self) -> bool {
126 self.required
127 }
128
129 fn initial(&self) -> Option<&Value> {
130 self.initial.as_ref()
131 }
132
133 fn help_text(&self) -> Option<&str> {
134 if self.help_text.is_empty() {
135 None
136 } else {
137 Some(&self.help_text)
138 }
139 }
140
141 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
142 if value.is_none() || value == Some(&Value::Null) {
143 if self.required {
144 let error_msg = self
145 .error_messages
146 .get("required")
147 .cloned()
148 .unwrap_or_else(|| "This field is required.".to_string());
149 return Err(FieldError::validation(None, &error_msg));
150 }
151 return Ok(Value::Null);
152 }
153
154 let s = match value.unwrap() {
155 Value::String(s) => s.trim(),
156 _ => {
157 let error_msg = self
158 .error_messages
159 .get("invalid")
160 .cloned()
161 .unwrap_or_else(|| "Enter a valid UUID.".to_string());
162 return Err(FieldError::validation(None, &error_msg));
163 }
164 };
165
166 if s.is_empty() {
167 if self.required {
168 let error_msg = self
169 .error_messages
170 .get("required")
171 .cloned()
172 .unwrap_or_else(|| "This field is required.".to_string());
173 return Err(FieldError::validation(None, &error_msg));
174 }
175 return Ok(Value::Null);
176 }
177
178 if !self.validate_uuid(s) {
179 let error_msg = self
180 .error_messages
181 .get("invalid")
182 .cloned()
183 .unwrap_or_else(|| "Enter a valid UUID.".to_string());
184 return Err(FieldError::validation(None, &error_msg));
185 }
186
187 Ok(Value::String(s.to_lowercase()))
188 }
189
190 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
191 match (initial, data) {
192 (None, None) => false,
193 (Some(_), None) | (None, Some(_)) => true,
194 (Some(Value::String(a)), Some(Value::String(b))) => {
195 a.to_lowercase() != b.to_lowercase()
196 }
197 (Some(a), Some(b)) => a != b,
198 }
199 }
200}
201
202#[derive(Debug, Clone)]
224pub struct DurationField {
225 pub name: String,
226 pub required: bool,
227 pub error_messages: HashMap<String, String>,
228 pub widget: Widget,
229 pub help_text: String,
230 pub initial: Option<Value>,
231}
232
233impl DurationField {
234 pub fn new(name: impl Into<String>) -> Self {
236 let mut error_messages = HashMap::new();
237 error_messages.insert(
238 "required".to_string(),
239 "This field is required.".to_string(),
240 );
241 error_messages.insert(
242 "invalid".to_string(),
243 "Enter a valid ISO 8601 duration.".to_string(),
244 );
245
246 Self {
247 name: name.into(),
248 required: true,
249 error_messages,
250 widget: Widget::TextInput,
251 help_text: String::new(),
252 initial: None,
253 }
254 }
255
256 pub fn required(mut self, required: bool) -> Self {
258 self.required = required;
259 self
260 }
261
262 pub fn help_text(mut self, text: impl Into<String>) -> Self {
264 self.help_text = text.into();
265 self
266 }
267
268 pub fn initial(mut self, value: Value) -> Self {
270 self.initial = Some(value);
271 self
272 }
273
274 pub fn error_message(
276 mut self,
277 error_type: impl Into<String>,
278 message: impl Into<String>,
279 ) -> Self {
280 self.error_messages
281 .insert(error_type.into(), message.into());
282 self
283 }
284
285 fn validate_duration(&self, s: &str) -> bool {
288 if !s.starts_with('P') {
289 return false;
290 }
291
292 let s = &s[1..]; if s.is_empty() {
295 return false;
296 }
297
298 if let Some(num_part) = s.strip_suffix('W') {
300 return num_part.chars().all(|c| c.is_ascii_digit());
301 }
302
303 let parts: Vec<&str> = s.split('T').collect();
305
306 if parts.is_empty() || parts.len() > 2 {
307 return false;
308 }
309
310 let date_valid = self.validate_duration_part(parts[0], &['Y', 'M', 'D']);
312
313 let time_valid = if parts.len() == 2 {
315 !parts[1].is_empty() && self.validate_duration_part(parts[1], &['H', 'M', 'S'])
316 } else {
317 true
318 };
319
320 date_valid && time_valid
321 }
322
323 fn validate_duration_part(&self, part: &str, units: &[char]) -> bool {
325 if part.is_empty() {
326 return true; }
328
329 let mut current_num = String::new();
330
331 for ch in part.chars() {
332 if ch.is_ascii_digit() || ch == '.' {
333 current_num.push(ch);
334 } else if units.contains(&ch) {
335 if current_num.is_empty() {
336 return false;
337 }
338 current_num.clear();
339 } else {
340 return false;
341 }
342 }
343
344 current_num.is_empty() }
346}
347
348impl FormField for DurationField {
349 fn name(&self) -> &str {
350 &self.name
351 }
352
353 fn label(&self) -> Option<&str> {
354 None
355 }
356
357 fn widget(&self) -> &Widget {
358 &self.widget
359 }
360
361 fn required(&self) -> bool {
362 self.required
363 }
364
365 fn initial(&self) -> Option<&Value> {
366 self.initial.as_ref()
367 }
368
369 fn help_text(&self) -> Option<&str> {
370 if self.help_text.is_empty() {
371 None
372 } else {
373 Some(&self.help_text)
374 }
375 }
376
377 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
378 if value.is_none() || value == Some(&Value::Null) {
379 if self.required {
380 let error_msg = self
381 .error_messages
382 .get("required")
383 .cloned()
384 .unwrap_or_else(|| "This field is required.".to_string());
385 return Err(FieldError::validation(None, &error_msg));
386 }
387 return Ok(Value::Null);
388 }
389
390 let s = match value.unwrap() {
391 Value::String(s) => s.trim(),
392 _ => {
393 let error_msg = self
394 .error_messages
395 .get("invalid")
396 .cloned()
397 .unwrap_or_else(|| "Enter a valid ISO 8601 duration.".to_string());
398 return Err(FieldError::validation(None, &error_msg));
399 }
400 };
401
402 if s.is_empty() {
403 if self.required {
404 let error_msg = self
405 .error_messages
406 .get("required")
407 .cloned()
408 .unwrap_or_else(|| "This field is required.".to_string());
409 return Err(FieldError::validation(None, &error_msg));
410 }
411 return Ok(Value::Null);
412 }
413
414 if !self.validate_duration(s) {
415 let error_msg = self
416 .error_messages
417 .get("invalid")
418 .cloned()
419 .unwrap_or_else(|| "Enter a valid ISO 8601 duration.".to_string());
420 return Err(FieldError::validation(None, &error_msg));
421 }
422
423 Ok(Value::String(s.to_uppercase()))
424 }
425
426 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
427 match (initial, data) {
428 (None, None) => false,
429 (Some(_), None) | (None, Some(_)) => true,
430 (Some(Value::String(a)), Some(Value::String(b))) => {
431 a.to_uppercase() != b.to_uppercase()
432 }
433 (Some(a), Some(b)) => a != b,
434 }
435 }
436}
437
438pub struct ComboField {
473 pub name: String,
474 pub required: bool,
475 pub error_messages: HashMap<String, String>,
476 pub widget: Widget,
477 pub help_text: String,
478 pub initial: Option<Value>,
479 pub validators: Vec<Box<dyn FormField>>,
480}
481
482impl ComboField {
483 pub fn new(name: impl Into<String>) -> Self {
485 let mut error_messages = HashMap::new();
486 error_messages.insert(
487 "required".to_string(),
488 "This field is required.".to_string(),
489 );
490
491 Self {
492 name: name.into(),
493 required: true,
494 error_messages,
495 widget: Widget::TextInput,
496 help_text: String::new(),
497 initial: None,
498 validators: Vec::new(),
499 }
500 }
501
502 pub fn add_validator(mut self, validator: Box<dyn FormField>) -> Self {
504 self.validators.push(validator);
505 self
506 }
507
508 pub fn required(mut self, required: bool) -> Self {
510 self.required = required;
511 self
512 }
513
514 pub fn help_text(mut self, text: impl Into<String>) -> Self {
516 self.help_text = text.into();
517 self
518 }
519
520 pub fn initial(mut self, value: Value) -> Self {
522 self.initial = Some(value);
523 self
524 }
525
526 pub fn error_message(
528 mut self,
529 error_type: impl Into<String>,
530 message: impl Into<String>,
531 ) -> Self {
532 self.error_messages
533 .insert(error_type.into(), message.into());
534 self
535 }
536}
537
538impl FormField for ComboField {
539 fn name(&self) -> &str {
540 &self.name
541 }
542
543 fn label(&self) -> Option<&str> {
544 None
545 }
546
547 fn widget(&self) -> &Widget {
548 &self.widget
549 }
550
551 fn required(&self) -> bool {
552 self.required
553 }
554
555 fn initial(&self) -> Option<&Value> {
556 self.initial.as_ref()
557 }
558
559 fn help_text(&self) -> Option<&str> {
560 if self.help_text.is_empty() {
561 None
562 } else {
563 Some(&self.help_text)
564 }
565 }
566
567 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
568 if value.is_none() || value == Some(&Value::Null) {
569 if self.required {
570 let error_msg = self
571 .error_messages
572 .get("required")
573 .cloned()
574 .unwrap_or_else(|| "This field is required.".to_string());
575 return Err(FieldError::validation(None, &error_msg));
576 }
577 return Ok(Value::Null);
578 }
579
580 let mut result = value.unwrap().clone();
582 for validator in &self.validators {
583 result = validator.clean(Some(&result))?;
584 }
585
586 Ok(result)
587 }
588
589 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
590 match (initial, data) {
591 (None, None) => false,
592 (Some(_), None) | (None, Some(_)) => true,
593 (Some(a), Some(b)) => a != b,
594 }
595 }
596}
597
598#[derive(Debug, Clone)]
620pub struct ColorField {
621 pub name: String,
622 pub required: bool,
623 pub error_messages: HashMap<String, String>,
624 pub widget: Widget,
625 pub help_text: String,
626 pub initial: Option<Value>,
627}
628
629impl ColorField {
630 pub fn new(name: impl Into<String>) -> Self {
632 let mut error_messages = HashMap::new();
633 error_messages.insert(
634 "required".to_string(),
635 "This field is required.".to_string(),
636 );
637 error_messages.insert(
638 "invalid".to_string(),
639 "Enter a valid hex color code (e.g., #FF0000).".to_string(),
640 );
641
642 Self {
643 name: name.into(),
644 required: true,
645 error_messages,
646 widget: Widget::TextInput,
647 help_text: String::new(),
648 initial: None,
649 }
650 }
651
652 pub fn required(mut self, required: bool) -> Self {
654 self.required = required;
655 self
656 }
657
658 pub fn help_text(mut self, text: impl Into<String>) -> Self {
660 self.help_text = text.into();
661 self
662 }
663
664 pub fn initial(mut self, value: Value) -> Self {
666 self.initial = Some(value);
667 self
668 }
669
670 fn validate_color(&self, s: &str) -> bool {
672 if !s.starts_with('#') {
673 return false;
674 }
675
676 let hex = &s[1..];
677 if hex.len() != 3 && hex.len() != 6 {
678 return false;
679 }
680
681 hex.chars().all(|c| c.is_ascii_hexdigit())
682 }
683}
684
685impl FormField for ColorField {
686 fn name(&self) -> &str {
687 &self.name
688 }
689
690 fn label(&self) -> Option<&str> {
691 None
692 }
693
694 fn widget(&self) -> &Widget {
695 &self.widget
696 }
697
698 fn required(&self) -> bool {
699 self.required
700 }
701
702 fn initial(&self) -> Option<&Value> {
703 self.initial.as_ref()
704 }
705
706 fn help_text(&self) -> Option<&str> {
707 if self.help_text.is_empty() {
708 None
709 } else {
710 Some(&self.help_text)
711 }
712 }
713
714 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
715 if value.is_none() || value == Some(&Value::Null) {
716 if self.required {
717 let error_msg = self
718 .error_messages
719 .get("required")
720 .cloned()
721 .unwrap_or_else(|| "This field is required.".to_string());
722 return Err(FieldError::validation(None, &error_msg));
723 }
724 return Ok(Value::Null);
725 }
726
727 let s = match value.unwrap() {
728 Value::String(s) => s.trim(),
729 _ => {
730 let error_msg = self
731 .error_messages
732 .get("invalid")
733 .cloned()
734 .unwrap_or_else(|| "Enter a valid hex color code.".to_string());
735 return Err(FieldError::validation(None, &error_msg));
736 }
737 };
738
739 if s.is_empty() {
740 if self.required {
741 let error_msg = self
742 .error_messages
743 .get("required")
744 .cloned()
745 .unwrap_or_else(|| "This field is required.".to_string());
746 return Err(FieldError::validation(None, &error_msg));
747 }
748 return Ok(Value::Null);
749 }
750
751 if !self.validate_color(s) {
752 let error_msg = self
753 .error_messages
754 .get("invalid")
755 .cloned()
756 .unwrap_or_else(|| "Enter a valid hex color code.".to_string());
757 return Err(FieldError::validation(None, &error_msg));
758 }
759
760 Ok(Value::String(s.to_uppercase()))
761 }
762
763 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
764 match (initial, data) {
765 (None, None) => false,
766 (Some(_), None) | (None, Some(_)) => true,
767 (Some(Value::String(a)), Some(Value::String(b))) => {
768 a.to_uppercase() != b.to_uppercase()
769 }
770 (Some(a), Some(b)) => a != b,
771 }
772 }
773}
774
775pub const PASSWORD_REDACTED: &str = "**********";
780
781#[derive(Debug, Clone)]
806pub struct PasswordField {
807 pub name: String,
808 pub required: bool,
809 pub error_messages: HashMap<String, String>,
810 pub widget: Widget,
811 pub help_text: String,
812 pub initial: Option<Value>,
813 pub min_length: usize,
814 pub require_uppercase: bool,
815 pub require_lowercase: bool,
816 pub require_digit: bool,
817 pub require_special: bool,
818}
819
820impl PasswordField {
821 pub fn new(name: impl Into<String>) -> Self {
823 let mut error_messages = HashMap::new();
824 error_messages.insert(
825 "required".to_string(),
826 "This field is required.".to_string(),
827 );
828 error_messages.insert(
829 "too_short".to_string(),
830 "Password must be at least {min_length} characters.".to_string(),
831 );
832 error_messages.insert(
833 "no_uppercase".to_string(),
834 "Password must contain at least one uppercase letter.".to_string(),
835 );
836 error_messages.insert(
837 "no_lowercase".to_string(),
838 "Password must contain at least one lowercase letter.".to_string(),
839 );
840 error_messages.insert(
841 "no_digit".to_string(),
842 "Password must contain at least one digit.".to_string(),
843 );
844 error_messages.insert(
845 "no_special".to_string(),
846 "Password must contain at least one special character.".to_string(),
847 );
848
849 Self {
850 name: name.into(),
851 required: true,
852 error_messages,
853 widget: Widget::PasswordInput,
854 help_text: String::new(),
855 initial: None,
856 min_length: 8,
857 require_uppercase: false,
858 require_lowercase: false,
859 require_digit: false,
860 require_special: false,
861 }
862 }
863
864 pub fn min_length(mut self, length: usize) -> Self {
866 self.min_length = length;
867 self
868 }
869
870 pub fn require_uppercase(mut self, required: bool) -> Self {
872 self.require_uppercase = required;
873 self
874 }
875
876 pub fn require_lowercase(mut self, required: bool) -> Self {
878 self.require_lowercase = required;
879 self
880 }
881
882 pub fn require_digit(mut self, required: bool) -> Self {
884 self.require_digit = required;
885 self
886 }
887
888 pub fn require_special(mut self, required: bool) -> Self {
890 self.require_special = required;
891 self
892 }
893
894 pub fn required(mut self, required: bool) -> Self {
896 self.required = required;
897 self
898 }
899
900 pub fn help_text(mut self, text: impl Into<String>) -> Self {
902 self.help_text = text.into();
903 self
904 }
905}
906
907impl FormField for PasswordField {
908 fn name(&self) -> &str {
909 &self.name
910 }
911
912 fn label(&self) -> Option<&str> {
913 None
914 }
915
916 fn widget(&self) -> &Widget {
917 &self.widget
918 }
919
920 fn required(&self) -> bool {
921 self.required
922 }
923
924 fn initial(&self) -> Option<&Value> {
925 self.initial.as_ref()
926 }
927
928 fn help_text(&self) -> Option<&str> {
929 if self.help_text.is_empty() {
930 None
931 } else {
932 Some(&self.help_text)
933 }
934 }
935
936 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
937 if value.is_none() || value == Some(&Value::Null) {
938 if self.required {
939 let error_msg = self
940 .error_messages
941 .get("required")
942 .cloned()
943 .unwrap_or_else(|| "This field is required.".to_string());
944 return Err(FieldError::validation(None, &error_msg));
945 }
946 return Ok(Value::Null);
947 }
948
949 let s = match value.unwrap() {
950 Value::String(s) => s,
951 _ => {
952 return Err(FieldError::validation(
953 Some(&self.name),
954 "Invalid password format.",
955 ));
956 }
957 };
958
959 if s.is_empty() {
960 if self.required {
961 let error_msg = self
962 .error_messages
963 .get("required")
964 .cloned()
965 .unwrap_or_else(|| "This field is required.".to_string());
966 return Err(FieldError::validation(None, &error_msg));
967 }
968 return Ok(Value::Null);
969 }
970
971 if s.len() < self.min_length {
973 let error_msg = self
974 .error_messages
975 .get("too_short")
976 .cloned()
977 .unwrap_or_else(|| {
978 format!("Password must be at least {} characters.", self.min_length)
979 });
980 return Err(FieldError::validation(None, &error_msg));
981 }
982
983 if self.require_uppercase && !s.chars().any(|c| c.is_uppercase()) {
985 let error_msg = self
986 .error_messages
987 .get("no_uppercase")
988 .cloned()
989 .unwrap_or_else(|| {
990 "Password must contain at least one uppercase letter.".to_string()
991 });
992 return Err(FieldError::validation(None, &error_msg));
993 }
994
995 if self.require_lowercase && !s.chars().any(|c| c.is_lowercase()) {
997 let error_msg = self
998 .error_messages
999 .get("no_lowercase")
1000 .cloned()
1001 .unwrap_or_else(|| {
1002 "Password must contain at least one lowercase letter.".to_string()
1003 });
1004 return Err(FieldError::validation(None, &error_msg));
1005 }
1006
1007 if self.require_digit && !s.chars().any(|c| c.is_ascii_digit()) {
1009 let error_msg = self
1010 .error_messages
1011 .get("no_digit")
1012 .cloned()
1013 .unwrap_or_else(|| "Password must contain at least one digit.".to_string());
1014 return Err(FieldError::validation(None, &error_msg));
1015 }
1016
1017 if self.require_special && !s.chars().any(|c| !c.is_alphanumeric()) {
1019 let error_msg = self
1020 .error_messages
1021 .get("no_special")
1022 .cloned()
1023 .unwrap_or_else(|| {
1024 "Password must contain at least one special character.".to_string()
1025 });
1026 return Err(FieldError::validation(None, &error_msg));
1027 }
1028
1029 Ok(Value::String(PASSWORD_REDACTED.to_string()))
1031 }
1032
1033 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
1034 match (initial, data) {
1035 (None, None) => false,
1036 (Some(_), None) | (None, Some(_)) => true,
1037 (Some(a), Some(b)) => a != b,
1038 }
1039 }
1040}
1041
1042#[cfg(test)]
1043mod tests {
1044 use super::*;
1045 use serde_json::json;
1046
1047 #[test]
1048 fn test_uuid_field_valid() {
1049 let field = UUIDField::new("id");
1050
1051 let result = field.clean(Some(&json!("550e8400-e29b-41d4-a716-446655440000")));
1052 assert!(result.is_ok());
1053
1054 let result = field.clean(Some(&json!("550E8400-E29B-41D4-A716-446655440000")));
1056 assert!(result.is_ok());
1057 }
1058
1059 #[test]
1060 fn test_uuid_field_invalid() {
1061 let field = UUIDField::new("id");
1062
1063 let result = field.clean(Some(&json!("550e8400-e29b")));
1065 assert!(result.is_err());
1066
1067 let result = field.clean(Some(&json!("550e8400-e29b-41d4-a716-44665544000g")));
1069 assert!(result.is_err());
1070
1071 let result = field.clean(Some(&json!("not-a-uuid")));
1073 assert!(result.is_err());
1074 }
1075
1076 #[test]
1077 fn test_duration_field_valid() {
1078 let field = DurationField::new("duration");
1079
1080 let result = field.clean(Some(&json!("P1Y2M3DT4H5M6S")));
1082 assert!(result.is_ok());
1083
1084 let result = field.clean(Some(&json!("P1D")));
1086 assert!(result.is_ok());
1087
1088 let result = field.clean(Some(&json!("PT1H")));
1090 assert!(result.is_ok());
1091
1092 let result = field.clean(Some(&json!("P2W")));
1094 assert!(result.is_ok());
1095 }
1096
1097 #[test]
1098 fn test_duration_field_invalid() {
1099 let field = DurationField::new("duration");
1100
1101 let result = field.clean(Some(&json!("1Y2M")));
1103 assert!(result.is_err());
1104
1105 let result = field.clean(Some(&json!("P")));
1107 assert!(result.is_err());
1108
1109 let result = field.clean(Some(&json!("P1X")));
1111 assert!(result.is_err());
1112 }
1113
1114 #[test]
1115 fn test_combo_field() {
1116 use crate::EmailField;
1117
1118 let mut char_field_min = crate::CharField::new("text".to_string());
1120 char_field_min.min_length = Some(5);
1121
1122 let mut char_field_max = crate::CharField::new("text".to_string());
1123 char_field_max.max_length = Some(50);
1124
1125 let field = ComboField::new("email")
1126 .add_validator(Box::new(char_field_min))
1127 .add_validator(Box::new(char_field_max))
1128 .add_validator(Box::new(EmailField::new("email".to_string())));
1129
1130 let result = field.clean(Some(&json!("test@example.com")));
1132 assert!(result.is_ok());
1133
1134 let result = field.clean(Some(&json!("a@b")));
1136 assert!(result.is_err());
1137
1138 let result = field.clean(Some(&json!("hello world")));
1140 assert!(result.is_err());
1141 }
1142
1143 #[test]
1144 fn test_color_field_valid() {
1145 let field = ColorField::new("color");
1146
1147 let result = field.clean(Some(&json!("#FF0000")));
1149 assert!(result.is_ok());
1150
1151 let result = field.clean(Some(&json!("#F00")));
1153 assert!(result.is_ok());
1154
1155 let result = field.clean(Some(&json!("#ff0000")));
1157 assert!(result.is_ok());
1158 }
1159
1160 #[test]
1161 fn test_color_field_invalid() {
1162 let field = ColorField::new("color");
1163
1164 let result = field.clean(Some(&json!("FF0000")));
1166 assert!(result.is_err());
1167
1168 let result = field.clean(Some(&json!("#FF00")));
1170 assert!(result.is_err());
1171
1172 let result = field.clean(Some(&json!("#GGGGGG")));
1174 assert!(result.is_err());
1175 }
1176
1177 #[test]
1178 fn test_password_field_basic() {
1179 let field = PasswordField::new("password").min_length(6);
1180
1181 let result = field.clean(Some(&json!("password123")));
1183 assert!(result.is_ok());
1184
1185 let result = field.clean(Some(&json!("pass")));
1187 assert!(result.is_err());
1188 }
1189
1190 #[test]
1191 fn test_password_field_requirements() {
1192 let field = PasswordField::new("password")
1193 .min_length(8)
1194 .require_uppercase(true)
1195 .require_digit(true)
1196 .require_special(true);
1197
1198 let result = field.clean(Some(&json!("SecurePass123!")));
1200 assert!(result.is_ok());
1201
1202 let result = field.clean(Some(&json!("password123!")));
1204 assert!(result.is_err());
1205
1206 let result = field.clean(Some(&json!("SecurePassword!")));
1208 assert!(result.is_err());
1209
1210 let result = field.clean(Some(&json!("SecurePassword123")));
1212 assert!(result.is_err());
1213 }
1214}