1use crate::bound_field::BoundField;
2use crate::field::{FieldError, FormField};
3use crate::wasm_compat::ValidationRule;
4use std::collections::HashMap;
5use std::ops::Index;
6
7fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
13 use sha2::{Digest, Sha256};
14 use subtle::ConstantTimeEq;
15
16 let hash_a = Sha256::digest(a);
17 let hash_b = Sha256::digest(b);
18 hash_a.ct_eq(&hash_b).into()
19}
20
21#[derive(Debug, thiserror::Error)]
23pub enum FormError {
24 #[error("Field error in {field}: {error}")]
26 Field {
27 field: String,
29 error: FieldError,
31 },
32 #[error("Validation error: {0}")]
34 Validation(String),
35 #[error("No model instance available for save operation")]
37 NoInstance,
38}
39
40pub type FormResult<T> = Result<T, FormError>;
42
43type CleanFunction =
44 Box<dyn Fn(&HashMap<String, serde_json::Value>) -> FormResult<()> + Send + Sync>;
45type FieldCleanFunction =
46 Box<dyn Fn(&serde_json::Value) -> FormResult<serde_json::Value> + Send + Sync>;
47
48pub const ALL_FIELDS_KEY: &str = "_all";
53
54pub struct Form {
56 fields: Vec<Box<dyn FormField>>,
57 data: HashMap<String, serde_json::Value>,
58 initial: HashMap<String, serde_json::Value>,
59 errors: HashMap<String, Vec<String>>,
60 is_bound: bool,
61 clean_functions: Vec<CleanFunction>,
62 field_clean_functions: HashMap<String, FieldCleanFunction>,
63 prefix: String,
64 validation_rules: Vec<ValidationRule>,
68 csrf_token: Option<String>,
70 csrf_enabled: bool,
72}
73
74impl Form {
75 pub fn new() -> Self {
87 Self {
88 fields: vec![],
89 data: HashMap::new(),
90 initial: HashMap::new(),
91 errors: HashMap::new(),
92 is_bound: false,
93 clean_functions: vec![],
94 field_clean_functions: HashMap::new(),
95 prefix: String::new(),
96 validation_rules: vec![],
97 csrf_token: None,
98 csrf_enabled: false,
99 }
100 }
101 pub fn with_initial(initial: HashMap<String, serde_json::Value>) -> Self {
117 Self {
118 fields: vec![],
119 data: HashMap::new(),
120 initial,
121 errors: HashMap::new(),
122 is_bound: false,
123 clean_functions: vec![],
124 field_clean_functions: HashMap::new(),
125 prefix: String::new(),
126 validation_rules: vec![],
127 csrf_token: None,
128 csrf_enabled: false,
129 }
130 }
131 pub fn with_prefix(prefix: String) -> Self {
143 Self {
144 fields: vec![],
145 data: HashMap::new(),
146 initial: HashMap::new(),
147 errors: HashMap::new(),
148 is_bound: false,
149 clean_functions: vec![],
150 field_clean_functions: HashMap::new(),
151 prefix,
152 validation_rules: vec![],
153 csrf_token: None,
154 csrf_enabled: false,
155 }
156 }
157 pub fn add_field(&mut self, field: Box<dyn FormField>) {
170 self.fields.push(field);
171 }
172 pub fn bind(&mut self, data: HashMap<String, serde_json::Value>) {
189 self.data = data;
190 self.is_bound = true;
191 }
192 pub fn is_valid(&mut self) -> bool {
213 if !self.is_bound {
214 return false;
215 }
216
217 self.errors.clear();
218
219 if !self.validate_csrf() {
221 self.errors
222 .entry(ALL_FIELDS_KEY.to_string())
223 .or_default()
224 .push("CSRF token missing or incorrect.".to_string());
225 return false;
226 }
227
228 for field in &self.fields {
229 let value = self.data.get(field.name());
230
231 match field.clean(value) {
232 Ok(mut cleaned) => {
233 if let Some(field_clean) = self.field_clean_functions.get(field.name()) {
235 match field_clean(&cleaned) {
236 Ok(further_cleaned) => {
237 cleaned = further_cleaned;
238 }
239 Err(e) => {
240 self.errors
241 .entry(field.name().to_string())
242 .or_default()
243 .push(e.to_string());
244 continue;
245 }
246 }
247 }
248 self.data.insert(field.name().to_string(), cleaned);
249 }
250 Err(e) => {
251 self.errors
252 .entry(field.name().to_string())
253 .or_default()
254 .push(e.to_string());
255 }
256 }
257 }
258
259 for clean_fn in &self.clean_functions {
261 if let Err(e) = clean_fn(&self.data) {
262 match e {
263 FormError::Field { field, error } => {
264 self.errors
265 .entry(field)
266 .or_default()
267 .push(error.to_string());
268 }
269 FormError::Validation(msg) => {
270 self.errors
271 .entry(ALL_FIELDS_KEY.to_string())
272 .or_default()
273 .push(msg);
274 }
275 FormError::NoInstance => {
276 self.errors
277 .entry(ALL_FIELDS_KEY.to_string())
278 .or_default()
279 .push(e.to_string());
280 }
281 }
282 }
283 }
284
285 self.errors.is_empty()
286 }
287 pub fn cleaned_data(&self) -> &HashMap<String, serde_json::Value> {
289 &self.data
290 }
291 pub fn errors(&self) -> &HashMap<String, Vec<String>> {
293 &self.errors
294 }
295 pub fn is_bound(&self) -> bool {
297 self.is_bound
298 }
299 pub fn fields(&self) -> &[Box<dyn FormField>] {
301 &self.fields
302 }
303 pub fn initial(&self) -> &HashMap<String, serde_json::Value> {
305 &self.initial
306 }
307 pub fn set_initial(&mut self, initial: HashMap<String, serde_json::Value>) {
322 self.initial = initial;
323 }
324 pub fn has_changed(&self) -> bool {
346 if !self.is_bound {
347 return false;
348 }
349
350 for field in &self.fields {
351 let initial_val = self.initial.get(field.name());
352 let data_val = self.data.get(field.name());
353 if field.has_changed(initial_val, data_val) {
354 return true;
355 }
356 }
357 false
358 }
359 pub fn get_field(&self, name: &str) -> Option<&dyn FormField> {
361 self.fields
362 .iter()
363 .find(|f| f.name() == name)
364 .map(|f| f.as_ref())
365 }
366 pub fn remove_field(&mut self, name: &str) -> Option<Box<dyn FormField>> {
368 let pos = self.fields.iter().position(|f| f.name() == name)?;
369 Some(self.fields.remove(pos))
370 }
371 pub fn field_count(&self) -> usize {
373 self.fields.len()
374 }
375 pub fn add_clean_function<F>(&mut self, f: F)
394 where
395 F: Fn(&HashMap<String, serde_json::Value>) -> FormResult<()> + Send + Sync + 'static,
396 {
397 self.clean_functions.push(Box::new(f));
398 }
399 pub fn add_field_clean_function<F>(&mut self, field_name: &str, f: F)
421 where
422 F: Fn(&serde_json::Value) -> FormResult<serde_json::Value> + Send + Sync + 'static,
423 {
424 self.field_clean_functions
425 .insert(field_name.to_string(), Box::new(f));
426 }
427
428 pub fn validation_rules(&self) -> &[ValidationRule] {
434 &self.validation_rules
435 }
436
437 pub fn add_min_length_validator(
460 &mut self,
461 field_name: impl Into<String>,
462 min: usize,
463 error_message: impl Into<String>,
464 ) {
465 self.validation_rules.push(ValidationRule::MinLength {
466 field_name: field_name.into(),
467 min,
468 error_message: error_message.into(),
469 });
470 }
471
472 pub fn add_max_length_validator(
485 &mut self,
486 field_name: impl Into<String>,
487 max: usize,
488 error_message: impl Into<String>,
489 ) {
490 self.validation_rules.push(ValidationRule::MaxLength {
491 field_name: field_name.into(),
492 max,
493 error_message: error_message.into(),
494 });
495 }
496
497 pub fn add_pattern_validator(
510 &mut self,
511 field_name: impl Into<String>,
512 pattern: impl Into<String>,
513 error_message: impl Into<String>,
514 ) {
515 self.validation_rules.push(ValidationRule::Pattern {
516 field_name: field_name.into(),
517 pattern: pattern.into(),
518 error_message: error_message.into(),
519 });
520 }
521
522 pub fn add_min_value_validator(
535 &mut self,
536 field_name: impl Into<String>,
537 min: f64,
538 error_message: impl Into<String>,
539 ) {
540 self.validation_rules.push(ValidationRule::MinValue {
541 field_name: field_name.into(),
542 min,
543 error_message: error_message.into(),
544 });
545 }
546
547 pub fn add_max_value_validator(
560 &mut self,
561 field_name: impl Into<String>,
562 max: f64,
563 error_message: impl Into<String>,
564 ) {
565 self.validation_rules.push(ValidationRule::MaxValue {
566 field_name: field_name.into(),
567 max,
568 error_message: error_message.into(),
569 });
570 }
571
572 pub fn add_email_validator(
585 &mut self,
586 field_name: impl Into<String>,
587 error_message: impl Into<String>,
588 ) {
589 self.validation_rules.push(ValidationRule::Email {
590 field_name: field_name.into(),
591 error_message: error_message.into(),
592 });
593 }
594
595 pub fn add_url_validator(
608 &mut self,
609 field_name: impl Into<String>,
610 error_message: impl Into<String>,
611 ) {
612 self.validation_rules.push(ValidationRule::Url {
613 field_name: field_name.into(),
614 error_message: error_message.into(),
615 });
616 }
617
618 pub fn add_fields_equal_validator(
642 &mut self,
643 field_names: Vec<String>,
644 error_message: impl Into<String>,
645 target_field: Option<String>,
646 ) {
647 self.validation_rules.push(ValidationRule::FieldsEqual {
648 field_names,
649 error_message: error_message.into(),
650 target_field,
651 });
652 }
653
654 pub fn add_validator_rule(
691 &mut self,
692 field_name: impl Into<String>,
693 validator_id: impl Into<String>,
694 params: serde_json::Value,
695 error_message: impl Into<String>,
696 ) {
697 self.validation_rules.push(ValidationRule::ValidatorRef {
698 field_name: field_name.into(),
699 validator_id: validator_id.into(),
700 params,
701 error_message: error_message.into(),
702 });
703 }
704
705 pub fn add_date_range_validator(
724 &mut self,
725 start_field: impl Into<String>,
726 end_field: impl Into<String>,
727 error_message: Option<String>,
728 ) {
729 let start = start_field.into();
730 let end = end_field.into();
731 let message = error_message
732 .unwrap_or_else(|| "End date must be after or equal to start date".to_string());
733
734 self.validation_rules.push(ValidationRule::DateRange {
735 start_field: start,
736 end_field: end.clone(),
737 error_message: message,
738 target_field: Some(end),
739 });
740 }
741
742 pub fn add_numeric_range_validator(
761 &mut self,
762 min_field: impl Into<String>,
763 max_field: impl Into<String>,
764 error_message: Option<String>,
765 ) {
766 let min = min_field.into();
767 let max = max_field.into();
768 let message = error_message.unwrap_or_else(|| {
769 "Maximum value must be greater than or equal to minimum value".to_string()
770 });
771
772 self.validation_rules.push(ValidationRule::NumericRange {
773 min_field: min,
774 max_field: max.clone(),
775 error_message: message,
776 target_field: Some(max),
777 });
778 }
779 pub fn set_csrf_token(&mut self, token: String) {
798 self.csrf_token = Some(token);
799 self.csrf_enabled = true;
800 }
801
802 pub fn csrf_enabled(&self) -> bool {
804 self.csrf_enabled
805 }
806
807 pub fn csrf_token(&self) -> Option<&str> {
809 self.csrf_token.as_deref()
810 }
811
812 fn validate_csrf(&self) -> bool {
816 if !self.csrf_enabled {
817 return true;
818 }
819
820 let expected = match &self.csrf_token {
821 Some(t) => t,
822 None => return false,
823 };
824
825 let submitted = self
826 .data
827 .get("csrfmiddlewaretoken")
828 .and_then(|v| v.as_str());
829
830 match submitted {
831 Some(token) => {
832 constant_time_eq(token.as_bytes(), expected.as_bytes())
834 }
835 None => false,
836 }
837 }
838
839 pub fn prefix(&self) -> &str {
841 &self.prefix
842 }
843 pub fn set_prefix(&mut self, prefix: String) {
845 self.prefix = prefix;
846 }
847 pub fn add_prefix_to_field_name(&self, field_name: &str) -> String {
849 if self.prefix.is_empty() {
850 field_name.to_string()
851 } else {
852 format!("{}-{}", self.prefix, field_name)
853 }
854 }
855 pub fn render_css_media(&self, css_files: &[&str]) -> String {
874 use crate::field::escape_attribute;
875 let mut html = String::new();
876 for path in css_files {
877 html.push_str(&format!(
878 "<link rel=\"stylesheet\" href=\"{}\" />\n",
879 escape_attribute(path)
880 ));
881 }
882 html
883 }
884
885 pub fn render_js_media(&self, js_files: &[&str]) -> String {
904 use crate::field::escape_attribute;
905 let mut html = String::new();
906 for path in js_files {
907 html.push_str(&format!(
908 "<script src=\"{}\"></script>\n",
909 escape_attribute(path)
910 ));
911 }
912 html
913 }
914
915 pub fn get_bound_field<'a>(&'a self, name: &str) -> Option<BoundField<'a>> {
917 let field = self.get_field(name)?;
918 let data = self.data.get(name);
919 let errors = self.errors.get(name).map(|e| e.as_slice()).unwrap_or(&[]);
920
921 Some(BoundField::new(
922 "form".to_string(),
923 field,
924 data,
925 errors,
926 &self.prefix,
927 ))
928 }
929}
930
931impl Default for Form {
932 fn default() -> Self {
933 Self::new()
934 }
935}
936
937impl Form {
953 #[allow(clippy::borrowed_box)]
955 pub fn get(&self, name: &str) -> Option<&Box<dyn FormField>> {
957 self.fields.iter().find(|f| f.name() == name)
958 }
959}
960
961impl Index<&str> for Form {
962 type Output = Box<dyn FormField>;
963
964 fn index(&self, name: &str) -> &Self::Output {
965 self.get(name)
966 .unwrap_or_else(|| panic!("Field '{}' not found", name))
967 }
968}
969
970#[cfg(test)]
971mod tests {
972 use super::*;
973 use crate::fields::CharField;
974
975 #[test]
976 fn test_form_validation() {
977 let mut form = Form::new();
978
979 let mut name_field = CharField::new("name".to_string());
980 name_field.max_length = Some(50);
981 form.add_field(Box::new(name_field));
982
983 let mut data = HashMap::new();
984 data.insert("name".to_string(), serde_json::json!("John Doe"));
985
986 form.bind(data);
987 assert!(form.is_valid());
988 assert!(form.errors().is_empty());
989 }
990
991 #[test]
992 fn test_form_validation_error() {
993 let mut form = Form::new();
994
995 let mut name_field = CharField::new("name".to_string());
996 name_field.max_length = Some(5);
997 form.add_field(Box::new(name_field));
998
999 let mut data = HashMap::new();
1000 data.insert("name".to_string(), serde_json::json!("Very Long Name"));
1001
1002 form.bind(data);
1003 assert!(!form.is_valid());
1004 assert!(!form.errors().is_empty());
1005 }
1006
1007 #[test]
1010 fn test_form_basic() {
1011 use crate::fields::CharField;
1013
1014 let mut form = Form::new();
1015 form.add_field(Box::new(CharField::new("first_name".to_string())));
1016 form.add_field(Box::new(CharField::new("last_name".to_string())));
1017
1018 let mut data = HashMap::new();
1019 data.insert("first_name".to_string(), serde_json::json!("John"));
1020 data.insert("last_name".to_string(), serde_json::json!("Lennon"));
1021
1022 form.bind(data);
1023
1024 assert!(form.is_bound());
1025 assert!(form.is_valid());
1026 assert!(form.errors().is_empty());
1027
1028 let cleaned = form.cleaned_data();
1030 assert_eq!(
1031 cleaned.get("first_name").unwrap(),
1032 &serde_json::json!("John")
1033 );
1034 assert_eq!(
1035 cleaned.get("last_name").unwrap(),
1036 &serde_json::json!("Lennon")
1037 );
1038 }
1039
1040 #[test]
1041 fn test_form_missing_required_fields() {
1042 use crate::fields::CharField;
1044
1045 let mut form = Form::new();
1046 form.add_field(Box::new(CharField::new("username".to_string()).required()));
1047 form.add_field(Box::new(CharField::new("email".to_string()).required()));
1048
1049 let data = HashMap::new(); form.bind(data);
1052
1053 assert!(form.is_bound());
1054 assert!(!form.is_valid());
1055 assert!(form.errors().contains_key("username"));
1056 assert!(form.errors().contains_key("email"));
1057 }
1058
1059 #[test]
1060 fn test_form_optional_fields() {
1061 use crate::fields::CharField;
1063
1064 let mut form = Form::new();
1065
1066 let username_field = CharField::new("username".to_string());
1067 form.add_field(Box::new(username_field));
1068
1069 let mut bio_field = CharField::new("bio".to_string());
1070 bio_field.required = false;
1071 form.add_field(Box::new(bio_field));
1072
1073 let mut data = HashMap::new();
1074 data.insert("username".to_string(), serde_json::json!("john"));
1075 form.bind(data);
1078
1079 assert!(form.is_bound());
1080 assert!(form.is_valid());
1081 assert!(form.errors().is_empty());
1082 }
1083
1084 #[test]
1085 fn test_form_unbound() {
1086 use crate::fields::CharField;
1088
1089 let mut form = Form::new();
1090 form.add_field(Box::new(CharField::new("name".to_string())));
1091
1092 assert!(!form.is_bound());
1093 assert!(!form.is_valid()); }
1095
1096 #[test]
1097 fn test_form_extra_data() {
1098 use crate::fields::CharField;
1100
1101 let mut form = Form::new();
1102 form.add_field(Box::new(CharField::new("name".to_string())));
1103
1104 let mut data = HashMap::new();
1105 data.insert("name".to_string(), serde_json::json!("John"));
1106 data.insert(
1107 "extra_field".to_string(),
1108 serde_json::json!("should be ignored"),
1109 );
1110
1111 form.bind(data);
1112
1113 assert!(form.is_valid());
1114 let cleaned = form.cleaned_data();
1115 assert_eq!(cleaned.get("name").unwrap(), &serde_json::json!("John"));
1116 assert!(cleaned.contains_key("extra_field"));
1118 }
1119
1120 #[test]
1121 fn test_forms_form_multiple_fields() {
1122 use crate::fields::{CharField, IntegerField};
1124
1125 let mut form = Form::new();
1126 form.add_field(Box::new(CharField::new("username".to_string())));
1127
1128 let mut age_field = IntegerField::new("age".to_string());
1129 age_field.min_value = Some(0);
1130 age_field.max_value = Some(150);
1131 form.add_field(Box::new(age_field));
1132
1133 let mut data = HashMap::new();
1134 data.insert("username".to_string(), serde_json::json!("alice"));
1135 data.insert("age".to_string(), serde_json::json!(30));
1136
1137 form.bind(data);
1138
1139 assert!(form.is_valid());
1140 assert!(form.errors().is_empty());
1141 }
1142
1143 #[test]
1144 fn test_form_multiple_fields_invalid() {
1145 use crate::fields::{CharField, IntegerField};
1147
1148 let mut form = Form::new();
1149
1150 let mut username_field = CharField::new("username".to_string());
1151 username_field.min_length = Some(3);
1152 form.add_field(Box::new(username_field));
1153
1154 let mut age_field = IntegerField::new("age".to_string());
1155 age_field.min_value = Some(0);
1156 age_field.max_value = Some(150);
1157 form.add_field(Box::new(age_field));
1158
1159 let mut data = HashMap::new();
1160 data.insert("username".to_string(), serde_json::json!("ab")); data.insert("age".to_string(), serde_json::json!(200)); form.bind(data);
1164
1165 assert!(!form.is_valid());
1166 assert!(form.errors().contains_key("username"));
1167 assert!(form.errors().contains_key("age"));
1168 }
1169
1170 #[test]
1171 fn test_form_multiple_instances() {
1172 use crate::fields::CharField;
1174
1175 let mut form1 = Form::new();
1176 form1.add_field(Box::new(CharField::new("name".to_string())));
1177
1178 let mut form2 = Form::new();
1179 form2.add_field(Box::new(CharField::new("name".to_string())));
1180
1181 let mut data1 = HashMap::new();
1182 data1.insert("name".to_string(), serde_json::json!("Form1"));
1183 form1.bind(data1);
1184
1185 let mut data2 = HashMap::new();
1186 data2.insert("name".to_string(), serde_json::json!("Form2"));
1187 form2.bind(data2);
1188
1189 assert!(form1.is_valid());
1190 assert!(form2.is_valid());
1191
1192 assert_eq!(
1193 form1.cleaned_data().get("name").unwrap(),
1194 &serde_json::json!("Form1")
1195 );
1196 assert_eq!(
1197 form2.cleaned_data().get("name").unwrap(),
1198 &serde_json::json!("Form2")
1199 );
1200 }
1201
1202 #[test]
1203 fn test_form_with_initial_data() {
1204 let mut initial = HashMap::new();
1205 initial.insert("name".to_string(), serde_json::json!("Initial Name"));
1206 initial.insert("age".to_string(), serde_json::json!(25));
1207
1208 let mut form = Form::with_initial(initial);
1209
1210 let name_field = CharField::new("name".to_string());
1211 form.add_field(Box::new(name_field));
1212
1213 let age_field = crate::IntegerField::new("age".to_string());
1214 form.add_field(Box::new(age_field));
1215
1216 assert_eq!(
1217 form.initial().get("name").unwrap(),
1218 &serde_json::json!("Initial Name")
1219 );
1220 assert_eq!(form.initial().get("age").unwrap(), &serde_json::json!(25));
1221 }
1222
1223 #[test]
1224 fn test_form_has_changed() {
1225 let mut initial = HashMap::new();
1226 initial.insert("name".to_string(), serde_json::json!("John"));
1227
1228 let mut form = Form::with_initial(initial);
1229
1230 let name_field = CharField::new("name".to_string());
1231 form.add_field(Box::new(name_field));
1232
1233 let mut data1 = HashMap::new();
1235 data1.insert("name".to_string(), serde_json::json!("John"));
1236 form.bind(data1);
1237 assert!(!form.has_changed());
1238
1239 let mut data2 = HashMap::new();
1241 data2.insert("name".to_string(), serde_json::json!("Jane"));
1242 form.bind(data2);
1243 assert!(form.has_changed());
1244 }
1245
1246 #[test]
1247 fn test_form_index_access() {
1248 let mut form = Form::new();
1249
1250 let name_field = CharField::new("name".to_string());
1251 form.add_field(Box::new(name_field));
1252
1253 let field = &form["name"];
1254 assert_eq!(field.name(), "name");
1255 }
1256
1257 #[test]
1258 #[should_panic(expected = "Field 'nonexistent' not found")]
1259 fn test_form_index_access_nonexistent() {
1260 let form = Form::new();
1261 let _ = &form["nonexistent"];
1262 }
1263
1264 #[test]
1265 fn test_form_get_field() {
1266 let mut form = Form::new();
1267
1268 let name_field = CharField::new("name".to_string());
1269 form.add_field(Box::new(name_field));
1270
1271 assert!(form.get_field("name").is_some());
1272 assert!(form.get_field("nonexistent").is_none());
1273 }
1274
1275 #[test]
1276 fn test_form_remove_field() {
1277 let mut form = Form::new();
1278
1279 let name_field = CharField::new("name".to_string());
1280 form.add_field(Box::new(name_field));
1281
1282 assert_eq!(form.field_count(), 1);
1283
1284 let removed = form.remove_field("name");
1285 assert!(removed.is_some());
1286 assert_eq!(form.field_count(), 0);
1287
1288 let not_removed = form.remove_field("nonexistent");
1289 assert!(not_removed.is_none());
1290 }
1291
1292 #[test]
1293 fn test_form_custom_validation() {
1294 let mut form = Form::new();
1295
1296 let mut password_field = CharField::new("password".to_string());
1297 password_field.min_length = Some(8);
1298 form.add_field(Box::new(password_field));
1299
1300 let mut confirm_field = CharField::new("confirm".to_string());
1301 confirm_field.min_length = Some(8);
1302 form.add_field(Box::new(confirm_field));
1303
1304 form.add_clean_function(|data| {
1306 let password = data.get("password").and_then(|v| v.as_str());
1307 let confirm = data.get("confirm").and_then(|v| v.as_str());
1308
1309 if password != confirm {
1310 return Err(FormError::Validation("Passwords do not match".to_string()));
1311 }
1312
1313 Ok(())
1314 });
1315
1316 let mut data1 = HashMap::new();
1318 data1.insert("password".to_string(), serde_json::json!("secret123"));
1319 data1.insert("confirm".to_string(), serde_json::json!("secret123"));
1320 form.bind(data1);
1321 assert!(form.is_valid());
1322
1323 let mut data2 = HashMap::new();
1325 data2.insert("password".to_string(), serde_json::json!("secret123"));
1326 data2.insert("confirm".to_string(), serde_json::json!("different"));
1327 form.bind(data2);
1328 assert!(!form.is_valid());
1329 assert!(form.errors().contains_key(ALL_FIELDS_KEY));
1330 }
1331
1332 #[test]
1333 fn test_form_prefix() {
1334 let mut form = Form::with_prefix("profile".to_string());
1335 assert_eq!(form.prefix(), "profile");
1336 assert_eq!(form.add_prefix_to_field_name("name"), "profile-name");
1337
1338 form.set_prefix("user".to_string());
1339 assert_eq!(form.prefix(), "user");
1340 assert_eq!(form.add_prefix_to_field_name("email"), "user-email");
1341 }
1342
1343 #[test]
1344 fn test_form_field_clean_function() {
1345 let mut form = Form::new();
1346
1347 let mut name_field = CharField::new("name".to_string());
1348 name_field.required = true;
1349 form.add_field(Box::new(name_field));
1350
1351 form.add_field_clean_function("name", |value| {
1353 if let Some(s) = value.as_str() {
1354 Ok(serde_json::json!(s.to_uppercase()))
1355 } else {
1356 Err(FormError::Validation("Expected string".to_string()))
1357 }
1358 });
1359
1360 let mut data = HashMap::new();
1361 data.insert("name".to_string(), serde_json::json!("john doe"));
1362 form.bind(data);
1363
1364 assert!(form.is_valid());
1365 assert_eq!(
1366 form.cleaned_data().get("name").unwrap(),
1367 &serde_json::json!("JOHN DOE")
1368 );
1369 }
1370}