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,
34 pub required: bool,
36 pub error_messages: HashMap<String, String>,
38 pub widget: Widget,
40 pub help_text: String,
42 pub initial: Option<Value>,
44}
45
46impl UUIDField {
47 pub fn new(name: impl Into<String>) -> Self {
49 let mut error_messages = HashMap::new();
50 error_messages.insert(
51 "required".to_string(),
52 "This field is required.".to_string(),
53 );
54 error_messages.insert("invalid".to_string(), "Enter a valid UUID.".to_string());
55
56 Self {
57 name: name.into(),
58 required: true,
59 error_messages,
60 widget: Widget::TextInput,
61 help_text: String::new(),
62 initial: None,
63 }
64 }
65
66 pub fn required(mut self, required: bool) -> Self {
68 self.required = required;
69 self
70 }
71
72 pub fn help_text(mut self, text: impl Into<String>) -> Self {
74 self.help_text = text.into();
75 self
76 }
77
78 pub fn initial(mut self, value: Value) -> Self {
80 self.initial = Some(value);
81 self
82 }
83
84 pub fn error_message(
86 mut self,
87 error_type: impl Into<String>,
88 message: impl Into<String>,
89 ) -> Self {
90 self.error_messages
91 .insert(error_type.into(), message.into());
92 self
93 }
94
95 fn validate_uuid(&self, s: &str) -> bool {
97 let parts: Vec<&str> = s.split('-').collect();
99 if parts.len() != 5 {
100 return false;
101 }
102
103 if parts[0].len() != 8
104 || parts[1].len() != 4
105 || parts[2].len() != 4
106 || parts[3].len() != 4
107 || parts[4].len() != 12
108 {
109 return false;
110 }
111
112 parts
113 .iter()
114 .all(|part| part.chars().all(|c| c.is_ascii_hexdigit()))
115 }
116}
117
118impl FormField for UUIDField {
119 fn name(&self) -> &str {
120 &self.name
121 }
122
123 fn label(&self) -> Option<&str> {
124 None
125 }
126
127 fn widget(&self) -> &Widget {
128 &self.widget
129 }
130
131 fn required(&self) -> bool {
132 self.required
133 }
134
135 fn initial(&self) -> Option<&Value> {
136 self.initial.as_ref()
137 }
138
139 fn help_text(&self) -> Option<&str> {
140 if self.help_text.is_empty() {
141 None
142 } else {
143 Some(&self.help_text)
144 }
145 }
146
147 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
148 if value.is_none() || value == Some(&Value::Null) {
149 if self.required {
150 let error_msg = self
151 .error_messages
152 .get("required")
153 .cloned()
154 .unwrap_or_else(|| "This field is required.".to_string());
155 return Err(FieldError::validation(None, &error_msg));
156 }
157 return Ok(Value::Null);
158 }
159
160 let s = match value.unwrap() {
161 Value::String(s) => s.trim(),
162 _ => {
163 let error_msg = self
164 .error_messages
165 .get("invalid")
166 .cloned()
167 .unwrap_or_else(|| "Enter a valid UUID.".to_string());
168 return Err(FieldError::validation(None, &error_msg));
169 }
170 };
171
172 if s.is_empty() {
173 if self.required {
174 let error_msg = self
175 .error_messages
176 .get("required")
177 .cloned()
178 .unwrap_or_else(|| "This field is required.".to_string());
179 return Err(FieldError::validation(None, &error_msg));
180 }
181 return Ok(Value::Null);
182 }
183
184 if !self.validate_uuid(s) {
185 let error_msg = self
186 .error_messages
187 .get("invalid")
188 .cloned()
189 .unwrap_or_else(|| "Enter a valid UUID.".to_string());
190 return Err(FieldError::validation(None, &error_msg));
191 }
192
193 Ok(Value::String(s.to_lowercase()))
194 }
195
196 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
197 match (initial, data) {
198 (None, None) => false,
199 (Some(_), None) | (None, Some(_)) => true,
200 (Some(Value::String(a)), Some(Value::String(b))) => {
201 a.to_lowercase() != b.to_lowercase()
202 }
203 (Some(a), Some(b)) => a != b,
204 }
205 }
206}
207
208#[derive(Debug, Clone)]
230pub struct DurationField {
231 pub name: String,
233 pub required: bool,
235 pub error_messages: HashMap<String, String>,
237 pub widget: Widget,
239 pub help_text: String,
241 pub initial: Option<Value>,
243}
244
245impl DurationField {
246 pub fn new(name: impl Into<String>) -> Self {
248 let mut error_messages = HashMap::new();
249 error_messages.insert(
250 "required".to_string(),
251 "This field is required.".to_string(),
252 );
253 error_messages.insert(
254 "invalid".to_string(),
255 "Enter a valid ISO 8601 duration.".to_string(),
256 );
257
258 Self {
259 name: name.into(),
260 required: true,
261 error_messages,
262 widget: Widget::TextInput,
263 help_text: String::new(),
264 initial: None,
265 }
266 }
267
268 pub fn required(mut self, required: bool) -> Self {
270 self.required = required;
271 self
272 }
273
274 pub fn help_text(mut self, text: impl Into<String>) -> Self {
276 self.help_text = text.into();
277 self
278 }
279
280 pub fn initial(mut self, value: Value) -> Self {
282 self.initial = Some(value);
283 self
284 }
285
286 pub fn error_message(
288 mut self,
289 error_type: impl Into<String>,
290 message: impl Into<String>,
291 ) -> Self {
292 self.error_messages
293 .insert(error_type.into(), message.into());
294 self
295 }
296
297 fn validate_duration(&self, s: &str) -> bool {
300 if !s.starts_with('P') {
301 return false;
302 }
303
304 let s = &s[1..]; if s.is_empty() {
307 return false;
308 }
309
310 if let Some(num_part) = s.strip_suffix('W') {
312 return num_part.chars().all(|c| c.is_ascii_digit());
313 }
314
315 let parts: Vec<&str> = s.split('T').collect();
317
318 if parts.is_empty() || parts.len() > 2 {
319 return false;
320 }
321
322 let date_valid = self.validate_duration_part(parts[0], &['Y', 'M', 'D']);
324
325 let time_valid = if parts.len() == 2 {
327 !parts[1].is_empty() && self.validate_duration_part(parts[1], &['H', 'M', 'S'])
328 } else {
329 true
330 };
331
332 date_valid && time_valid
333 }
334
335 fn validate_duration_part(&self, part: &str, units: &[char]) -> bool {
337 if part.is_empty() {
338 return true; }
340
341 let mut current_num = String::new();
342
343 for ch in part.chars() {
344 if ch.is_ascii_digit() || ch == '.' {
345 current_num.push(ch);
346 } else if units.contains(&ch) {
347 if current_num.is_empty() {
348 return false;
349 }
350 current_num.clear();
351 } else {
352 return false;
353 }
354 }
355
356 current_num.is_empty() }
358}
359
360impl FormField for DurationField {
361 fn name(&self) -> &str {
362 &self.name
363 }
364
365 fn label(&self) -> Option<&str> {
366 None
367 }
368
369 fn widget(&self) -> &Widget {
370 &self.widget
371 }
372
373 fn required(&self) -> bool {
374 self.required
375 }
376
377 fn initial(&self) -> Option<&Value> {
378 self.initial.as_ref()
379 }
380
381 fn help_text(&self) -> Option<&str> {
382 if self.help_text.is_empty() {
383 None
384 } else {
385 Some(&self.help_text)
386 }
387 }
388
389 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
390 if value.is_none() || value == Some(&Value::Null) {
391 if self.required {
392 let error_msg = self
393 .error_messages
394 .get("required")
395 .cloned()
396 .unwrap_or_else(|| "This field is required.".to_string());
397 return Err(FieldError::validation(None, &error_msg));
398 }
399 return Ok(Value::Null);
400 }
401
402 let s = match value.unwrap() {
403 Value::String(s) => s.trim(),
404 _ => {
405 let error_msg = self
406 .error_messages
407 .get("invalid")
408 .cloned()
409 .unwrap_or_else(|| "Enter a valid ISO 8601 duration.".to_string());
410 return Err(FieldError::validation(None, &error_msg));
411 }
412 };
413
414 if s.is_empty() {
415 if self.required {
416 let error_msg = self
417 .error_messages
418 .get("required")
419 .cloned()
420 .unwrap_or_else(|| "This field is required.".to_string());
421 return Err(FieldError::validation(None, &error_msg));
422 }
423 return Ok(Value::Null);
424 }
425
426 if !self.validate_duration(s) {
427 let error_msg = self
428 .error_messages
429 .get("invalid")
430 .cloned()
431 .unwrap_or_else(|| "Enter a valid ISO 8601 duration.".to_string());
432 return Err(FieldError::validation(None, &error_msg));
433 }
434
435 Ok(Value::String(s.to_uppercase()))
436 }
437
438 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
439 match (initial, data) {
440 (None, None) => false,
441 (Some(_), None) | (None, Some(_)) => true,
442 (Some(Value::String(a)), Some(Value::String(b))) => {
443 a.to_uppercase() != b.to_uppercase()
444 }
445 (Some(a), Some(b)) => a != b,
446 }
447 }
448}
449
450pub struct ComboField {
485 pub name: String,
487 pub required: bool,
489 pub error_messages: HashMap<String, String>,
491 pub widget: Widget,
493 pub help_text: String,
495 pub initial: Option<Value>,
497 pub validators: Vec<Box<dyn FormField>>,
499}
500
501impl ComboField {
502 pub fn new(name: impl Into<String>) -> Self {
504 let mut error_messages = HashMap::new();
505 error_messages.insert(
506 "required".to_string(),
507 "This field is required.".to_string(),
508 );
509
510 Self {
511 name: name.into(),
512 required: true,
513 error_messages,
514 widget: Widget::TextInput,
515 help_text: String::new(),
516 initial: None,
517 validators: Vec::new(),
518 }
519 }
520
521 pub fn add_validator(mut self, validator: Box<dyn FormField>) -> Self {
523 self.validators.push(validator);
524 self
525 }
526
527 pub fn required(mut self, required: bool) -> Self {
529 self.required = required;
530 self
531 }
532
533 pub fn help_text(mut self, text: impl Into<String>) -> Self {
535 self.help_text = text.into();
536 self
537 }
538
539 pub fn initial(mut self, value: Value) -> Self {
541 self.initial = Some(value);
542 self
543 }
544
545 pub fn error_message(
547 mut self,
548 error_type: impl Into<String>,
549 message: impl Into<String>,
550 ) -> Self {
551 self.error_messages
552 .insert(error_type.into(), message.into());
553 self
554 }
555}
556
557impl FormField for ComboField {
558 fn name(&self) -> &str {
559 &self.name
560 }
561
562 fn label(&self) -> Option<&str> {
563 None
564 }
565
566 fn widget(&self) -> &Widget {
567 &self.widget
568 }
569
570 fn required(&self) -> bool {
571 self.required
572 }
573
574 fn initial(&self) -> Option<&Value> {
575 self.initial.as_ref()
576 }
577
578 fn help_text(&self) -> Option<&str> {
579 if self.help_text.is_empty() {
580 None
581 } else {
582 Some(&self.help_text)
583 }
584 }
585
586 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
587 if value.is_none() || value == Some(&Value::Null) {
588 if self.required {
589 let error_msg = self
590 .error_messages
591 .get("required")
592 .cloned()
593 .unwrap_or_else(|| "This field is required.".to_string());
594 return Err(FieldError::validation(None, &error_msg));
595 }
596 return Ok(Value::Null);
597 }
598
599 let mut result = value.unwrap().clone();
601 for validator in &self.validators {
602 result = validator.clean(Some(&result))?;
603 }
604
605 Ok(result)
606 }
607
608 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
609 match (initial, data) {
610 (None, None) => false,
611 (Some(_), None) | (None, Some(_)) => true,
612 (Some(a), Some(b)) => a != b,
613 }
614 }
615}
616
617#[derive(Debug, Clone)]
639pub struct ColorField {
640 pub name: String,
642 pub required: bool,
644 pub error_messages: HashMap<String, String>,
646 pub widget: Widget,
648 pub help_text: String,
650 pub initial: Option<Value>,
652}
653
654impl ColorField {
655 pub fn new(name: impl Into<String>) -> Self {
657 let mut error_messages = HashMap::new();
658 error_messages.insert(
659 "required".to_string(),
660 "This field is required.".to_string(),
661 );
662 error_messages.insert(
663 "invalid".to_string(),
664 "Enter a valid hex color code (e.g., #FF0000).".to_string(),
665 );
666
667 Self {
668 name: name.into(),
669 required: true,
670 error_messages,
671 widget: Widget::TextInput,
672 help_text: String::new(),
673 initial: None,
674 }
675 }
676
677 pub fn required(mut self, required: bool) -> Self {
679 self.required = required;
680 self
681 }
682
683 pub fn help_text(mut self, text: impl Into<String>) -> Self {
685 self.help_text = text.into();
686 self
687 }
688
689 pub fn initial(mut self, value: Value) -> Self {
691 self.initial = Some(value);
692 self
693 }
694
695 fn validate_color(&self, s: &str) -> bool {
697 if !s.starts_with('#') {
698 return false;
699 }
700
701 let hex = &s[1..];
702 if hex.len() != 3 && hex.len() != 6 {
703 return false;
704 }
705
706 hex.chars().all(|c| c.is_ascii_hexdigit())
707 }
708}
709
710impl FormField for ColorField {
711 fn name(&self) -> &str {
712 &self.name
713 }
714
715 fn label(&self) -> Option<&str> {
716 None
717 }
718
719 fn widget(&self) -> &Widget {
720 &self.widget
721 }
722
723 fn required(&self) -> bool {
724 self.required
725 }
726
727 fn initial(&self) -> Option<&Value> {
728 self.initial.as_ref()
729 }
730
731 fn help_text(&self) -> Option<&str> {
732 if self.help_text.is_empty() {
733 None
734 } else {
735 Some(&self.help_text)
736 }
737 }
738
739 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
740 if value.is_none() || value == Some(&Value::Null) {
741 if self.required {
742 let error_msg = self
743 .error_messages
744 .get("required")
745 .cloned()
746 .unwrap_or_else(|| "This field is required.".to_string());
747 return Err(FieldError::validation(None, &error_msg));
748 }
749 return Ok(Value::Null);
750 }
751
752 let s = match value.unwrap() {
753 Value::String(s) => s.trim(),
754 _ => {
755 let error_msg = self
756 .error_messages
757 .get("invalid")
758 .cloned()
759 .unwrap_or_else(|| "Enter a valid hex color code.".to_string());
760 return Err(FieldError::validation(None, &error_msg));
761 }
762 };
763
764 if s.is_empty() {
765 if self.required {
766 let error_msg = self
767 .error_messages
768 .get("required")
769 .cloned()
770 .unwrap_or_else(|| "This field is required.".to_string());
771 return Err(FieldError::validation(None, &error_msg));
772 }
773 return Ok(Value::Null);
774 }
775
776 if !self.validate_color(s) {
777 let error_msg = self
778 .error_messages
779 .get("invalid")
780 .cloned()
781 .unwrap_or_else(|| "Enter a valid hex color code.".to_string());
782 return Err(FieldError::validation(None, &error_msg));
783 }
784
785 Ok(Value::String(s.to_uppercase()))
786 }
787
788 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
789 match (initial, data) {
790 (None, None) => false,
791 (Some(_), None) | (None, Some(_)) => true,
792 (Some(Value::String(a)), Some(Value::String(b))) => {
793 a.to_uppercase() != b.to_uppercase()
794 }
795 (Some(a), Some(b)) => a != b,
796 }
797 }
798}
799
800pub const PASSWORD_REDACTED: &str = "**********";
805
806#[derive(Debug, Clone)]
831pub struct PasswordField {
832 pub name: String,
834 pub required: bool,
836 pub error_messages: HashMap<String, String>,
838 pub widget: Widget,
840 pub help_text: String,
842 pub initial: Option<Value>,
844 pub min_length: usize,
846 pub require_uppercase: bool,
848 pub require_lowercase: bool,
850 pub require_digit: bool,
852 pub require_special: bool,
854}
855
856impl PasswordField {
857 pub fn new(name: impl Into<String>) -> Self {
859 let mut error_messages = HashMap::new();
860 error_messages.insert(
861 "required".to_string(),
862 "This field is required.".to_string(),
863 );
864 error_messages.insert(
865 "too_short".to_string(),
866 "Password must be at least {min_length} characters.".to_string(),
867 );
868 error_messages.insert(
869 "no_uppercase".to_string(),
870 "Password must contain at least one uppercase letter.".to_string(),
871 );
872 error_messages.insert(
873 "no_lowercase".to_string(),
874 "Password must contain at least one lowercase letter.".to_string(),
875 );
876 error_messages.insert(
877 "no_digit".to_string(),
878 "Password must contain at least one digit.".to_string(),
879 );
880 error_messages.insert(
881 "no_special".to_string(),
882 "Password must contain at least one special character.".to_string(),
883 );
884
885 Self {
886 name: name.into(),
887 required: true,
888 error_messages,
889 widget: Widget::PasswordInput,
890 help_text: String::new(),
891 initial: None,
892 min_length: 8,
893 require_uppercase: false,
894 require_lowercase: false,
895 require_digit: false,
896 require_special: false,
897 }
898 }
899
900 pub fn min_length(mut self, length: usize) -> Self {
902 self.min_length = length;
903 self
904 }
905
906 pub fn require_uppercase(mut self, required: bool) -> Self {
908 self.require_uppercase = required;
909 self
910 }
911
912 pub fn require_lowercase(mut self, required: bool) -> Self {
914 self.require_lowercase = required;
915 self
916 }
917
918 pub fn require_digit(mut self, required: bool) -> Self {
920 self.require_digit = required;
921 self
922 }
923
924 pub fn require_special(mut self, required: bool) -> Self {
926 self.require_special = required;
927 self
928 }
929
930 pub fn required(mut self, required: bool) -> Self {
932 self.required = required;
933 self
934 }
935
936 pub fn help_text(mut self, text: impl Into<String>) -> Self {
938 self.help_text = text.into();
939 self
940 }
941}
942
943impl FormField for PasswordField {
944 fn name(&self) -> &str {
945 &self.name
946 }
947
948 fn label(&self) -> Option<&str> {
949 None
950 }
951
952 fn widget(&self) -> &Widget {
953 &self.widget
954 }
955
956 fn required(&self) -> bool {
957 self.required
958 }
959
960 fn initial(&self) -> Option<&Value> {
961 self.initial.as_ref()
962 }
963
964 fn help_text(&self) -> Option<&str> {
965 if self.help_text.is_empty() {
966 None
967 } else {
968 Some(&self.help_text)
969 }
970 }
971
972 fn clean(&self, value: Option<&Value>) -> FieldResult<Value> {
973 if value.is_none() || value == Some(&Value::Null) {
974 if self.required {
975 let error_msg = self
976 .error_messages
977 .get("required")
978 .cloned()
979 .unwrap_or_else(|| "This field is required.".to_string());
980 return Err(FieldError::validation(None, &error_msg));
981 }
982 return Ok(Value::Null);
983 }
984
985 let s = match value.unwrap() {
986 Value::String(s) => s,
987 _ => {
988 return Err(FieldError::validation(
989 Some(&self.name),
990 "Invalid password format.",
991 ));
992 }
993 };
994
995 if s.is_empty() {
996 if self.required {
997 let error_msg = self
998 .error_messages
999 .get("required")
1000 .cloned()
1001 .unwrap_or_else(|| "This field is required.".to_string());
1002 return Err(FieldError::validation(None, &error_msg));
1003 }
1004 return Ok(Value::Null);
1005 }
1006
1007 if s.len() < self.min_length {
1009 let error_msg = self
1010 .error_messages
1011 .get("too_short")
1012 .cloned()
1013 .unwrap_or_else(|| {
1014 format!("Password must be at least {} characters.", self.min_length)
1015 });
1016 return Err(FieldError::validation(None, &error_msg));
1017 }
1018
1019 if self.require_uppercase && !s.chars().any(|c| c.is_uppercase()) {
1021 let error_msg = self
1022 .error_messages
1023 .get("no_uppercase")
1024 .cloned()
1025 .unwrap_or_else(|| {
1026 "Password must contain at least one uppercase letter.".to_string()
1027 });
1028 return Err(FieldError::validation(None, &error_msg));
1029 }
1030
1031 if self.require_lowercase && !s.chars().any(|c| c.is_lowercase()) {
1033 let error_msg = self
1034 .error_messages
1035 .get("no_lowercase")
1036 .cloned()
1037 .unwrap_or_else(|| {
1038 "Password must contain at least one lowercase letter.".to_string()
1039 });
1040 return Err(FieldError::validation(None, &error_msg));
1041 }
1042
1043 if self.require_digit && !s.chars().any(|c| c.is_ascii_digit()) {
1045 let error_msg = self
1046 .error_messages
1047 .get("no_digit")
1048 .cloned()
1049 .unwrap_or_else(|| "Password must contain at least one digit.".to_string());
1050 return Err(FieldError::validation(None, &error_msg));
1051 }
1052
1053 if self.require_special && !s.chars().any(|c| !c.is_alphanumeric()) {
1055 let error_msg = self
1056 .error_messages
1057 .get("no_special")
1058 .cloned()
1059 .unwrap_or_else(|| {
1060 "Password must contain at least one special character.".to_string()
1061 });
1062 return Err(FieldError::validation(None, &error_msg));
1063 }
1064
1065 Ok(Value::String(PASSWORD_REDACTED.to_string()))
1067 }
1068
1069 fn has_changed(&self, initial: Option<&Value>, data: Option<&Value>) -> bool {
1070 match (initial, data) {
1071 (None, None) => false,
1072 (Some(_), None) | (None, Some(_)) => true,
1073 (Some(a), Some(b)) => a != b,
1074 }
1075 }
1076}
1077
1078#[cfg(test)]
1079mod tests {
1080 use super::*;
1081 use serde_json::json;
1082
1083 #[test]
1084 fn test_uuid_field_valid() {
1085 let field = UUIDField::new("id");
1086
1087 let result = field.clean(Some(&json!("550e8400-e29b-41d4-a716-446655440000")));
1088 assert!(result.is_ok());
1089
1090 let result = field.clean(Some(&json!("550E8400-E29B-41D4-A716-446655440000")));
1092 assert!(result.is_ok());
1093 }
1094
1095 #[test]
1096 fn test_uuid_field_invalid() {
1097 let field = UUIDField::new("id");
1098
1099 let result = field.clean(Some(&json!("550e8400-e29b")));
1101 assert!(result.is_err());
1102
1103 let result = field.clean(Some(&json!("550e8400-e29b-41d4-a716-44665544000g")));
1105 assert!(result.is_err());
1106
1107 let result = field.clean(Some(&json!("not-a-uuid")));
1109 assert!(result.is_err());
1110 }
1111
1112 #[test]
1113 fn test_duration_field_valid() {
1114 let field = DurationField::new("duration");
1115
1116 let result = field.clean(Some(&json!("P1Y2M3DT4H5M6S")));
1118 assert!(result.is_ok());
1119
1120 let result = field.clean(Some(&json!("P1D")));
1122 assert!(result.is_ok());
1123
1124 let result = field.clean(Some(&json!("PT1H")));
1126 assert!(result.is_ok());
1127
1128 let result = field.clean(Some(&json!("P2W")));
1130 assert!(result.is_ok());
1131 }
1132
1133 #[test]
1134 fn test_duration_field_invalid() {
1135 let field = DurationField::new("duration");
1136
1137 let result = field.clean(Some(&json!("1Y2M")));
1139 assert!(result.is_err());
1140
1141 let result = field.clean(Some(&json!("P")));
1143 assert!(result.is_err());
1144
1145 let result = field.clean(Some(&json!("P1X")));
1147 assert!(result.is_err());
1148 }
1149
1150 #[test]
1151 fn test_combo_field() {
1152 use crate::EmailField;
1153
1154 let mut char_field_min = crate::CharField::new("text".to_string());
1156 char_field_min.min_length = Some(5);
1157
1158 let mut char_field_max = crate::CharField::new("text".to_string());
1159 char_field_max.max_length = Some(50);
1160
1161 let field = ComboField::new("email")
1162 .add_validator(Box::new(char_field_min))
1163 .add_validator(Box::new(char_field_max))
1164 .add_validator(Box::new(EmailField::new("email".to_string())));
1165
1166 let result = field.clean(Some(&json!("test@example.com")));
1168 assert!(result.is_ok());
1169
1170 let result = field.clean(Some(&json!("a@b")));
1172 assert!(result.is_err());
1173
1174 let result = field.clean(Some(&json!("hello world")));
1176 assert!(result.is_err());
1177 }
1178
1179 #[test]
1180 fn test_color_field_valid() {
1181 let field = ColorField::new("color");
1182
1183 let result = field.clean(Some(&json!("#FF0000")));
1185 assert!(result.is_ok());
1186
1187 let result = field.clean(Some(&json!("#F00")));
1189 assert!(result.is_ok());
1190
1191 let result = field.clean(Some(&json!("#ff0000")));
1193 assert!(result.is_ok());
1194 }
1195
1196 #[test]
1197 fn test_color_field_invalid() {
1198 let field = ColorField::new("color");
1199
1200 let result = field.clean(Some(&json!("FF0000")));
1202 assert!(result.is_err());
1203
1204 let result = field.clean(Some(&json!("#FF00")));
1206 assert!(result.is_err());
1207
1208 let result = field.clean(Some(&json!("#GGGGGG")));
1210 assert!(result.is_err());
1211 }
1212
1213 #[test]
1214 fn test_password_field_basic() {
1215 let field = PasswordField::new("password").min_length(6);
1216
1217 let result = field.clean(Some(&json!("password123")));
1219 assert!(result.is_ok());
1220
1221 let result = field.clean(Some(&json!("pass")));
1223 assert!(result.is_err());
1224 }
1225
1226 #[test]
1227 fn test_password_field_requirements() {
1228 let field = PasswordField::new("password")
1229 .min_length(8)
1230 .require_uppercase(true)
1231 .require_digit(true)
1232 .require_special(true);
1233
1234 let result = field.clean(Some(&json!("SecurePass123!")));
1236 assert!(result.is_ok());
1237
1238 let result = field.clean(Some(&json!("password123!")));
1240 assert!(result.is_err());
1241
1242 let result = field.clean(Some(&json!("SecurePassword!")));
1244 assert!(result.is_err());
1245
1246 let result = field.clean(Some(&json!("SecurePassword123")));
1248 assert!(result.is_err());
1249 }
1250}