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 { max_length: Option<usize> },
15 Text,
16 Integer,
17 Float,
18 Boolean,
19 DateTime,
20 Date,
21 Time,
22 Email,
23 Url,
24 Json,
25}
26
27pub trait FormModel: Send + Sync {
31 fn field_names() -> Vec<String>;
33
34 fn field_type(_name: &str) -> Option<FieldType> {
50 None
51 }
52
53 fn get_field(&self, name: &str) -> Option<Value>;
55
56 fn set_field(&mut self, name: &str, value: Value) -> Result<(), String>;
58
59 fn save(&mut self) -> Result<(), String>;
61
62 fn validate(&self) -> Result<(), Vec<String>> {
64 Ok(())
65 }
66
67 fn to_choice_label(&self) -> String {
83 self.get_field("id")
85 .and_then(|v| v.as_i64().map(|i| i.to_string()))
86 .or_else(|| {
87 self.get_field("id")
88 .and_then(|v| v.as_str().map(|s| s.to_string()))
89 })
90 .unwrap_or_default()
91 }
92
93 fn to_choice_value(&self) -> String {
108 self.get_field("id")
109 .and_then(|v| v.as_i64().map(|i| i.to_string()))
110 .or_else(|| {
111 self.get_field("id")
112 .and_then(|v| v.as_str().map(|s| s.to_string()))
113 })
114 .unwrap_or_default()
115 }
116}
117
118#[derive(Debug, Clone, Default)]
120pub struct ModelFormConfig {
121 pub fields: Option<Vec<String>>,
123 pub exclude: Vec<String>,
125 pub widgets: HashMap<String, crate::Widget>,
127 pub labels: HashMap<String, String>,
129 pub help_texts: HashMap<String, String>,
131}
132
133impl ModelFormConfig {
134 pub fn new() -> Self {
135 Self::default()
136 }
137 pub fn fields(mut self, fields: Vec<String>) -> Self {
138 self.fields = Some(fields);
139 self
140 }
141 pub fn exclude(mut self, exclude: Vec<String>) -> Self {
142 self.exclude = exclude;
143 self
144 }
145 pub fn widget(mut self, field: String, widget: crate::Widget) -> Self {
146 self.widgets.insert(field, widget);
147 self
148 }
149 pub fn label(mut self, field: String, label: String) -> Self {
150 self.labels.insert(field, label);
151 self
152 }
153 pub fn help_text(mut self, field: String, text: String) -> Self {
154 self.help_texts.insert(field, text);
155 self
156 }
157}
158
159pub struct ModelForm<T: FormModel> {
161 form: Form,
162 instance: Option<T>,
163 #[allow(dead_code)]
164 config: ModelFormConfig,
165 _phantom: PhantomData<T>,
166}
167
168impl<T: FormModel> ModelForm<T> {
169 fn create_form_field(
171 name: &str,
172 field_type: FieldType,
173 config: &ModelFormConfig,
174 ) -> Box<dyn FormField> {
175 let label = config.labels.get(name).cloned();
176 let help_text = config.help_texts.get(name).cloned();
177 let widget = config.widgets.get(name).cloned();
178
179 match field_type {
180 FieldType::Char { max_length } => {
181 let mut field = CharField::new(name.to_string());
182 if let Some(label) = label {
183 field.label = Some(label);
184 }
185 if let Some(help) = help_text {
186 field.help_text = Some(help);
187 }
188 if let Some(w) = widget {
189 field.widget = w;
190 }
191 field.max_length = max_length;
192 Box::new(field)
193 }
194 FieldType::Text => {
195 let mut field = CharField::new(name.to_string());
196 if let Some(label) = label {
197 field.label = Some(label);
198 }
199 if let Some(help) = help_text {
200 field.help_text = Some(help);
201 }
202 if let Some(w) = widget {
203 field.widget = w;
204 } else {
205 field.widget = Widget::TextArea;
206 }
207 Box::new(field)
208 }
209 FieldType::Email => {
210 let mut field = EmailField::new(name.to_string());
211 if let Some(label) = label {
212 field.label = Some(label);
213 }
214 if let Some(help) = help_text {
215 field.help_text = Some(help);
216 }
217 if let Some(w) = widget {
218 field.widget = w;
219 }
220 Box::new(field)
221 }
222 FieldType::Integer => {
223 let mut field = IntegerField::new(name.to_string());
224 if let Some(label) = label {
225 field.label = Some(label);
226 }
227 if let Some(help) = help_text {
228 field.help_text = Some(help);
229 }
230 if let Some(w) = widget {
231 field.widget = w;
232 }
233 Box::new(field)
234 }
235 FieldType::Float => {
236 let mut field = FloatField::new(name.to_string());
237 if let Some(label) = label {
238 field.label = Some(label);
239 }
240 if let Some(help) = help_text {
241 field.help_text = Some(help);
242 }
243 if let Some(w) = widget {
244 field.widget = w;
245 }
246 Box::new(field)
247 }
248 _ => {
250 let mut field = CharField::new(name.to_string());
251 if let Some(label) = label {
252 field.label = Some(label);
253 }
254 if let Some(help) = help_text {
255 field.help_text = Some(help);
256 }
257 if let Some(w) = widget {
258 field.widget = w;
259 }
260 Box::new(field)
261 }
262 }
263 }
264
265 pub fn new(instance: Option<T>, config: ModelFormConfig) -> Self {
277 let mut form = Form::new();
278
279 let all_fields = T::field_names();
281
282 let fields_to_include: Vec<String> = if let Some(ref include) = config.fields {
284 include
285 .iter()
286 .filter(|f| !config.exclude.contains(f))
287 .cloned()
288 .collect()
289 } else {
290 all_fields
291 .iter()
292 .filter(|f| !config.exclude.contains(f))
293 .cloned()
294 .collect()
295 };
296
297 for field_name in &fields_to_include {
299 if let Some(field_type) = T::field_type(field_name) {
300 let form_field = Self::create_form_field(field_name, field_type, &config);
301 form.add_field(form_field);
302 }
303 }
304
305 if let Some(ref inst) = instance {
307 let mut initial = HashMap::new();
308 for field_name in &fields_to_include {
309 if let Some(value) = inst.get_field(field_name) {
310 initial.insert(field_name.clone(), value);
311 }
312 }
313 form.bind(initial);
314 }
315
316 Self {
317 form,
318 instance,
319 config,
320 _phantom: PhantomData,
321 }
322 }
323 pub fn empty(config: ModelFormConfig) -> Self {
334 Self::new(None, config)
335 }
336 pub fn bind(&mut self, data: HashMap<String, Value>) -> &mut Self {
352 self.form.bind(data);
354 self
355 }
356 pub fn is_valid(&mut self) -> bool {
368 if let Some(ref instance) = self.instance
370 && let Err(_errors) = instance.validate()
371 {
372 return false;
373 }
374
375 true
376 }
377 pub fn save(&mut self) -> Result<T, FormError> {
392 if !self.is_valid() {
393 return Err(FormError::Validation("Form is not valid".to_string()));
394 }
395
396 let mut instance = self.instance.take().ok_or(FormError::NoInstance)?;
398
399 let cleaned_data = self.form.cleaned_data();
401 for (field_name, value) in cleaned_data.iter() {
402 if let Err(e) = instance.set_field(field_name, value.clone()) {
403 return Err(FormError::Validation(format!(
404 "Failed to set field {}: {}",
405 field_name, e
406 )));
407 }
408 }
409
410 if let Err(e) = instance.save() {
412 return Err(FormError::Validation(format!("Failed to save: {}", e)));
413 }
414
415 Ok(instance)
416 }
417 pub fn set_field_value(&mut self, field_name: &str, value: Value) {
424 if let Some(ref mut instance) = self.instance {
425 let _ = instance.set_field(field_name, value);
428 }
429 }
430
431 pub fn form(&self) -> &Form {
432 &self.form
433 }
434 pub fn form_mut(&mut self) -> &mut Form {
435 &mut self.form
436 }
437 pub fn instance(&self) -> Option<&T> {
438 self.instance.as_ref()
439 }
440}
441
442pub struct ModelFormBuilder<T: FormModel> {
444 config: ModelFormConfig,
445 _phantom: PhantomData<T>,
446}
447
448impl<T: FormModel> ModelFormBuilder<T> {
449 pub fn new() -> Self {
450 Self {
451 config: ModelFormConfig::default(),
452 _phantom: PhantomData,
453 }
454 }
455 pub fn fields(mut self, fields: Vec<String>) -> Self {
456 self.config.fields = Some(fields);
457 self
458 }
459 pub fn exclude(mut self, exclude: Vec<String>) -> Self {
460 self.config.exclude = exclude;
461 self
462 }
463 pub fn widget(mut self, field: String, widget: crate::Widget) -> Self {
464 self.config.widgets.insert(field, widget);
465 self
466 }
467 pub fn label(mut self, field: String, label: String) -> Self {
468 self.config.labels.insert(field, label);
469 self
470 }
471 pub fn help_text(mut self, field: String, text: String) -> Self {
472 self.config.help_texts.insert(field, text);
473 self
474 }
475 pub fn build(self, instance: Option<T>) -> ModelForm<T> {
487 ModelForm::new(instance, self.config)
488 }
489}
490
491impl<T: FormModel> Default for ModelFormBuilder<T> {
492 fn default() -> Self {
493 Self::new()
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500 use rstest::rstest;
501
502 #[derive(Debug)]
504 struct TestModel {
505 id: i32,
506 name: String,
507 email: String,
508 }
509
510 impl FormModel for TestModel {
511 fn field_names() -> Vec<String> {
512 vec!["id".to_string(), "name".to_string(), "email".to_string()]
513 }
514
515 fn field_type(name: &str) -> Option<FieldType> {
516 match name {
517 "id" => Some(FieldType::Integer),
518 "name" => Some(FieldType::Char {
519 max_length: Some(100),
520 }),
521 "email" => Some(FieldType::Email),
522 _ => None,
523 }
524 }
525
526 fn get_field(&self, name: &str) -> Option<Value> {
527 match name {
528 "id" => Some(Value::Number(self.id.into())),
529 "name" => Some(Value::String(self.name.clone())),
530 "email" => Some(Value::String(self.email.clone())),
531 _ => None,
532 }
533 }
534
535 fn set_field(&mut self, name: &str, value: Value) -> Result<(), String> {
536 match name {
537 "id" => {
538 if let Value::Number(n) = value {
539 self.id = n.as_i64().unwrap() as i32;
540 Ok(())
541 } else {
542 Err("Invalid type for id".to_string())
543 }
544 }
545 "name" => {
546 if let Value::String(s) = value {
547 self.name = s;
548 Ok(())
549 } else {
550 Err("Invalid type for name".to_string())
551 }
552 }
553 "email" => {
554 if let Value::String(s) = value {
555 self.email = s;
556 Ok(())
557 } else {
558 Err("Invalid type for email".to_string())
559 }
560 }
561 _ => Err(format!("Unknown field: {}", name)),
562 }
563 }
564
565 fn save(&mut self) -> Result<(), String> {
566 Ok(())
568 }
569 }
570
571 #[rstest]
572 fn test_model_form_config() {
573 let config = ModelFormConfig::new()
575 .fields(vec!["name".to_string(), "email".to_string()])
576 .exclude(vec!["id".to_string()]);
577
578 assert_eq!(
580 config.fields,
581 Some(vec!["name".to_string(), "email".to_string()])
582 );
583 assert_eq!(config.exclude, vec!["id".to_string()]);
584 }
585
586 #[rstest]
587 fn test_model_form_builder() {
588 let instance = TestModel {
590 id: 1,
591 name: "John".to_string(),
592 email: "john@example.com".to_string(),
593 };
594
595 let form = ModelFormBuilder::<TestModel>::new()
597 .fields(vec!["name".to_string(), "email".to_string()])
598 .build(Some(instance));
599
600 assert!(form.instance().is_some());
602 }
603
604 #[rstest]
605 fn test_model_field_names() {
606 let fields = TestModel::field_names();
608
609 assert_eq!(
611 fields,
612 vec!["id".to_string(), "name".to_string(), "email".to_string()]
613 );
614 }
615
616 #[rstest]
617 fn test_save_without_instance_returns_no_instance_error() {
618 let config = ModelFormConfig::new();
620 let mut form = ModelForm::<TestModel>::empty(config);
621
622 let result = form.save();
624
625 assert!(result.is_err());
627 let err = result.unwrap_err();
628 assert!(
629 matches!(err, FormError::NoInstance),
630 "Expected FormError::NoInstance, got: {err}"
631 );
632 }
633}