1use crate::{CharField, EmailField, FloatField, Form, FormError, FormField, IntegerField, Widget};
7use serde_json::Value;
8use std::collections::HashMap;
9use std::marker::PhantomData;
10
11#[derive(Debug, Clone)]
13pub enum FieldType {
14 Char {
16 max_length: Option<usize>,
18 },
19 Text,
21 Integer,
23 Float,
25 Boolean,
27 DateTime,
29 Date,
31 Time,
33 Email,
35 Url,
37 Json,
39}
40
41pub trait FormModel: Send + Sync {
45 fn field_names() -> Vec<String>;
47
48 fn field_type(_name: &str) -> Option<FieldType> {
64 None
65 }
66
67 fn get_field(&self, name: &str) -> Option<Value>;
69
70 fn set_field(&mut self, name: &str, value: Value) -> Result<(), String>;
72
73 fn save(&mut self) -> Result<(), String>;
75
76 fn validate(&self) -> Result<(), Vec<String>> {
78 Ok(())
79 }
80
81 fn to_choice_label(&self) -> String {
97 self.get_field("id")
99 .and_then(|v| v.as_i64().map(|i| i.to_string()))
100 .or_else(|| {
101 self.get_field("id")
102 .and_then(|v| v.as_str().map(|s| s.to_string()))
103 })
104 .unwrap_or_default()
105 }
106
107 fn to_choice_value(&self) -> String {
122 self.get_field("id")
123 .and_then(|v| v.as_i64().map(|i| i.to_string()))
124 .or_else(|| {
125 self.get_field("id")
126 .and_then(|v| v.as_str().map(|s| s.to_string()))
127 })
128 .unwrap_or_default()
129 }
130}
131
132#[derive(Debug, Clone, Default)]
134pub struct ModelFormConfig {
135 pub fields: Option<Vec<String>>,
137 pub exclude: Vec<String>,
139 pub widgets: HashMap<String, crate::Widget>,
141 pub labels: HashMap<String, String>,
143 pub help_texts: HashMap<String, String>,
145}
146
147impl ModelFormConfig {
148 pub fn new() -> Self {
150 Self::default()
151 }
152 pub fn fields(mut self, fields: Vec<String>) -> Self {
154 self.fields = Some(fields);
155 self
156 }
157 pub fn exclude(mut self, exclude: Vec<String>) -> Self {
159 self.exclude = exclude;
160 self
161 }
162 pub fn widget(mut self, field: String, widget: crate::Widget) -> Self {
164 self.widgets.insert(field, widget);
165 self
166 }
167 pub fn label(mut self, field: String, label: String) -> Self {
169 self.labels.insert(field, label);
170 self
171 }
172 pub fn help_text(mut self, field: String, text: String) -> Self {
174 self.help_texts.insert(field, text);
175 self
176 }
177}
178
179pub struct ModelForm<T: FormModel> {
181 form: Form,
182 instance: Option<T>,
183 #[allow(dead_code)]
185 config: ModelFormConfig,
186 _phantom: PhantomData<T>,
187}
188
189impl<T: FormModel> ModelForm<T> {
190 fn create_form_field(
192 name: &str,
193 field_type: FieldType,
194 config: &ModelFormConfig,
195 ) -> Box<dyn FormField> {
196 let label = config.labels.get(name).cloned();
197 let help_text = config.help_texts.get(name).cloned();
198 let widget = config.widgets.get(name).cloned();
199
200 match field_type {
201 FieldType::Char { max_length } => {
202 let mut field = CharField::new(name.to_string());
203 if let Some(label) = label {
204 field.label = Some(label);
205 }
206 if let Some(help) = help_text {
207 field.help_text = Some(help);
208 }
209 if let Some(w) = widget {
210 field.widget = w;
211 }
212 field.max_length = max_length;
213 Box::new(field)
214 }
215 FieldType::Text => {
216 let mut field = CharField::new(name.to_string());
217 if let Some(label) = label {
218 field.label = Some(label);
219 }
220 if let Some(help) = help_text {
221 field.help_text = Some(help);
222 }
223 if let Some(w) = widget {
224 field.widget = w;
225 } else {
226 field.widget = Widget::TextArea;
227 }
228 Box::new(field)
229 }
230 FieldType::Email => {
231 let mut field = EmailField::new(name.to_string());
232 if let Some(label) = label {
233 field.label = Some(label);
234 }
235 if let Some(help) = help_text {
236 field.help_text = Some(help);
237 }
238 if let Some(w) = widget {
239 field.widget = w;
240 }
241 Box::new(field)
242 }
243 FieldType::Integer => {
244 let mut field = IntegerField::new(name.to_string());
245 if let Some(label) = label {
246 field.label = Some(label);
247 }
248 if let Some(help) = help_text {
249 field.help_text = Some(help);
250 }
251 if let Some(w) = widget {
252 field.widget = w;
253 }
254 Box::new(field)
255 }
256 FieldType::Float => {
257 let mut field = FloatField::new(name.to_string());
258 if let Some(label) = label {
259 field.label = Some(label);
260 }
261 if let Some(help) = help_text {
262 field.help_text = Some(help);
263 }
264 if let Some(w) = widget {
265 field.widget = w;
266 }
267 Box::new(field)
268 }
269 _ => {
271 let mut field = CharField::new(name.to_string());
272 if let Some(label) = label {
273 field.label = Some(label);
274 }
275 if let Some(help) = help_text {
276 field.help_text = Some(help);
277 }
278 if let Some(w) = widget {
279 field.widget = w;
280 }
281 Box::new(field)
282 }
283 }
284 }
285
286 pub fn new(instance: Option<T>, config: ModelFormConfig) -> Self {
298 let mut form = Form::new();
299
300 let all_fields = T::field_names();
302
303 let fields_to_include: Vec<String> = if let Some(ref include) = config.fields {
305 include
306 .iter()
307 .filter(|f| !config.exclude.contains(f))
308 .cloned()
309 .collect()
310 } else {
311 all_fields
312 .iter()
313 .filter(|f| !config.exclude.contains(f))
314 .cloned()
315 .collect()
316 };
317
318 for field_name in &fields_to_include {
320 if let Some(field_type) = T::field_type(field_name) {
321 let form_field = Self::create_form_field(field_name, field_type, &config);
322 form.add_field(form_field);
323 }
324 }
325
326 if let Some(ref inst) = instance {
328 let mut initial = HashMap::new();
329 for field_name in &fields_to_include {
330 if let Some(value) = inst.get_field(field_name) {
331 initial.insert(field_name.clone(), value);
332 }
333 }
334 form.bind(initial);
335 }
336
337 Self {
338 form,
339 instance,
340 config,
341 _phantom: PhantomData,
342 }
343 }
344 pub fn empty(config: ModelFormConfig) -> Self {
355 Self::new(None, config)
356 }
357 pub fn bind(&mut self, data: HashMap<String, Value>) -> &mut Self {
373 self.form.bind(data);
375 self
376 }
377 pub fn is_valid(&mut self) -> bool {
389 if let Some(ref instance) = self.instance
391 && let Err(_errors) = instance.validate()
392 {
393 return false;
394 }
395
396 true
397 }
398 pub fn save(&mut self) -> Result<T, FormError> {
413 if !self.is_valid() {
414 return Err(FormError::Validation("Form is not valid".to_string()));
415 }
416
417 let mut instance = self.instance.take().ok_or(FormError::NoInstance)?;
419
420 let cleaned_data = self.form.cleaned_data();
422 for (field_name, value) in cleaned_data.iter() {
423 if let Err(e) = instance.set_field(field_name, value.clone()) {
424 return Err(FormError::Validation(format!(
425 "Failed to set field {}: {}",
426 field_name, e
427 )));
428 }
429 }
430
431 if let Err(e) = instance.save() {
433 return Err(FormError::Validation(format!("Failed to save: {}", e)));
434 }
435
436 Ok(instance)
437 }
438 pub fn set_field_value(&mut self, field_name: &str, value: Value) {
445 if let Some(ref mut instance) = self.instance {
446 let _ = instance.set_field(field_name, value);
449 }
450 }
451
452 pub fn form(&self) -> &Form {
454 &self.form
455 }
456 pub fn form_mut(&mut self) -> &mut Form {
458 &mut self.form
459 }
460 pub fn instance(&self) -> Option<&T> {
462 self.instance.as_ref()
463 }
464}
465
466pub struct ModelFormBuilder<T: FormModel> {
468 config: ModelFormConfig,
469 _phantom: PhantomData<T>,
470}
471
472impl<T: FormModel> ModelFormBuilder<T> {
473 pub fn new() -> Self {
475 Self {
476 config: ModelFormConfig::default(),
477 _phantom: PhantomData,
478 }
479 }
480 pub fn fields(mut self, fields: Vec<String>) -> Self {
482 self.config.fields = Some(fields);
483 self
484 }
485 pub fn exclude(mut self, exclude: Vec<String>) -> Self {
487 self.config.exclude = exclude;
488 self
489 }
490 pub fn widget(mut self, field: String, widget: crate::Widget) -> Self {
492 self.config.widgets.insert(field, widget);
493 self
494 }
495 pub fn label(mut self, field: String, label: String) -> Self {
497 self.config.labels.insert(field, label);
498 self
499 }
500 pub fn help_text(mut self, field: String, text: String) -> Self {
502 self.config.help_texts.insert(field, text);
503 self
504 }
505 pub fn build(self, instance: Option<T>) -> ModelForm<T> {
517 ModelForm::new(instance, self.config)
518 }
519}
520
521impl<T: FormModel> Default for ModelFormBuilder<T> {
522 fn default() -> Self {
523 Self::new()
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use rstest::rstest;
531
532 #[derive(Debug)]
534 struct TestModel {
535 id: i32,
536 name: String,
537 email: String,
538 }
539
540 impl FormModel for TestModel {
541 fn field_names() -> Vec<String> {
542 vec!["id".to_string(), "name".to_string(), "email".to_string()]
543 }
544
545 fn field_type(name: &str) -> Option<FieldType> {
546 match name {
547 "id" => Some(FieldType::Integer),
548 "name" => Some(FieldType::Char {
549 max_length: Some(100),
550 }),
551 "email" => Some(FieldType::Email),
552 _ => None,
553 }
554 }
555
556 fn get_field(&self, name: &str) -> Option<Value> {
557 match name {
558 "id" => Some(Value::Number(self.id.into())),
559 "name" => Some(Value::String(self.name.clone())),
560 "email" => Some(Value::String(self.email.clone())),
561 _ => None,
562 }
563 }
564
565 fn set_field(&mut self, name: &str, value: Value) -> Result<(), String> {
566 match name {
567 "id" => {
568 if let Value::Number(n) = value {
569 self.id = n.as_i64().unwrap() as i32;
570 Ok(())
571 } else {
572 Err("Invalid type for id".to_string())
573 }
574 }
575 "name" => {
576 if let Value::String(s) = value {
577 self.name = s;
578 Ok(())
579 } else {
580 Err("Invalid type for name".to_string())
581 }
582 }
583 "email" => {
584 if let Value::String(s) = value {
585 self.email = s;
586 Ok(())
587 } else {
588 Err("Invalid type for email".to_string())
589 }
590 }
591 _ => Err(format!("Unknown field: {}", name)),
592 }
593 }
594
595 fn save(&mut self) -> Result<(), String> {
596 Ok(())
598 }
599 }
600
601 #[rstest]
602 fn test_model_form_config() {
603 let config = ModelFormConfig::new()
605 .fields(vec!["name".to_string(), "email".to_string()])
606 .exclude(vec!["id".to_string()]);
607
608 assert_eq!(
610 config.fields,
611 Some(vec!["name".to_string(), "email".to_string()])
612 );
613 assert_eq!(config.exclude, vec!["id".to_string()]);
614 }
615
616 #[rstest]
617 fn test_model_form_builder() {
618 let instance = TestModel {
620 id: 1,
621 name: "John".to_string(),
622 email: "john@example.com".to_string(),
623 };
624
625 let form = ModelFormBuilder::<TestModel>::new()
627 .fields(vec!["name".to_string(), "email".to_string()])
628 .build(Some(instance));
629
630 assert!(form.instance().is_some());
632 }
633
634 #[rstest]
635 fn test_model_field_names() {
636 let fields = TestModel::field_names();
638
639 assert_eq!(
641 fields,
642 vec!["id".to_string(), "name".to_string(), "email".to_string()]
643 );
644 }
645
646 #[rstest]
647 fn test_save_without_instance_returns_no_instance_error() {
648 let config = ModelFormConfig::new();
650 let mut form = ModelForm::<TestModel>::empty(config);
651
652 let result = form.save();
654
655 assert!(result.is_err());
657 let err = result.unwrap_err();
658 assert!(
659 matches!(err, FormError::NoInstance),
660 "Expected FormError::NoInstance, got: {err}"
661 );
662 }
663}