1use crate::FormError;
7use crate::formset::FormSet;
8use crate::model_form::{FormModel, ModelForm, ModelFormConfig};
9use std::collections::HashMap;
10use std::marker::PhantomData;
11
12#[derive(Debug, Clone)]
14pub struct ModelFormSetConfig {
15 pub form_config: ModelFormConfig,
17 pub can_delete: bool,
19 pub can_order: bool,
21 pub extra: usize,
23 pub max_num: Option<usize>,
25 pub min_num: usize,
27}
28
29impl Default for ModelFormSetConfig {
30 fn default() -> Self {
31 Self {
32 form_config: ModelFormConfig::default(),
33 can_delete: false,
34 can_order: false,
35 extra: 1,
36 max_num: Some(1000),
37 min_num: 0,
38 }
39 }
40}
41
42impl ModelFormSetConfig {
43 pub fn new() -> Self {
55 Self::default()
56 }
57 pub fn with_extra(mut self, extra: usize) -> Self {
68 self.extra = extra;
69 self
70 }
71 pub fn with_can_delete(mut self, can_delete: bool) -> Self {
82 self.can_delete = can_delete;
83 self
84 }
85 pub fn with_can_order(mut self, can_order: bool) -> Self {
96 self.can_order = can_order;
97 self
98 }
99 pub fn with_max_num(mut self, max_num: Option<usize>) -> Self {
110 self.max_num = max_num;
111 self
112 }
113 pub fn with_min_num(mut self, min_num: usize) -> Self {
124 self.min_num = min_num;
125 self
126 }
127 pub fn with_form_config(mut self, form_config: ModelFormConfig) -> Self {
140 self.form_config = form_config;
141 self
142 }
143}
144
145pub struct ModelFormSet<T: FormModel> {
147 model_forms: Vec<ModelForm<T>>,
148 formset: FormSet,
149 _phantom: PhantomData<T>,
150}
151
152impl<T: FormModel> ModelFormSet<T> {
153 pub fn new(prefix: String, instances: Vec<T>, config: ModelFormSetConfig) -> Self {
166 let mut model_forms = Vec::new();
167
168 for instance in instances {
170 let model_form = ModelForm::new(Some(instance), config.form_config.clone());
171 model_forms.push(model_form);
172 }
173
174 for _ in 0..config.extra {
176 let model_form = ModelForm::empty(config.form_config.clone());
177 model_forms.push(model_form);
178 }
179
180 let formset = FormSet::new(prefix)
182 .with_extra(config.extra)
183 .with_can_delete(config.can_delete)
184 .with_can_order(config.can_order)
185 .with_max_num(config.max_num)
186 .with_min_num(config.min_num);
187
188 Self {
189 model_forms,
190 formset,
191 _phantom: PhantomData,
192 }
193 }
194 pub fn empty(prefix: String, config: ModelFormSetConfig) -> Self {
206 Self::new(prefix, Vec::new(), config)
207 }
208 pub fn prefix(&self) -> &str {
209 self.formset.prefix()
210 }
211 pub fn instances(&self) -> Vec<&T> {
212 self.model_forms
213 .iter()
214 .filter_map(|form| form.instance())
215 .collect()
216 }
217 pub fn form_count(&self) -> usize {
218 self.model_forms
220 .iter()
221 .filter(|form| form.instance().is_some())
222 .count()
223 }
224 pub fn total_form_count(&self) -> usize {
225 self.model_forms.len()
227 }
228 pub fn is_valid(&mut self) -> bool {
240 self.model_forms.iter_mut().all(|form| form.is_valid())
242 }
243 pub fn errors(&self) -> Vec<String> {
244 self.model_forms
246 .iter()
247 .flat_map(|model_form| {
248 model_form
249 .form()
250 .errors()
251 .values()
252 .flat_map(|errors| errors.iter().cloned())
253 })
254 .collect()
255 }
256 pub fn save(&mut self) -> Result<Vec<T>, FormError> {
268 if !self.is_valid() {
269 return Err(FormError::Validation("Formset is not valid".to_string()));
270 }
271
272 let mut saved_instances = Vec::new();
273
274 for model_form in &mut self.model_forms {
276 if model_form.instance().is_some() {
278 let instance = model_form.save()?;
280 saved_instances.push(instance);
281 }
282 }
283
284 Ok(saved_instances)
285 }
286 pub fn management_form_data(&self) -> HashMap<String, String> {
301 self.formset.management_form_data()
302 }
303}
304
305pub struct ModelFormSetBuilder<T: FormModel> {
307 config: ModelFormSetConfig,
308 _phantom: PhantomData<T>,
309}
310
311impl<T: FormModel> ModelFormSetBuilder<T> {
312 pub fn new() -> Self {
322 Self {
323 config: ModelFormSetConfig::default(),
324 _phantom: PhantomData,
325 }
326 }
327 pub fn extra(mut self, extra: usize) -> Self {
337 self.config.extra = extra;
338 self
339 }
340 pub fn can_delete(mut self, can_delete: bool) -> Self {
350 self.config.can_delete = can_delete;
351 self
352 }
353 pub fn can_order(mut self, can_order: bool) -> Self {
363 self.config.can_order = can_order;
364 self
365 }
366 pub fn max_num(mut self, max_num: usize) -> Self {
376 self.config.max_num = Some(max_num);
377 self
378 }
379 pub fn min_num(mut self, min_num: usize) -> Self {
389 self.config.min_num = min_num;
390 self
391 }
392 pub fn build(self, prefix: String, instances: Vec<T>) -> ModelFormSet<T> {
404 ModelFormSet::new(prefix, instances, self.config)
405 }
406 pub fn build_empty(self, prefix: String) -> ModelFormSet<T> {
417 ModelFormSet::empty(prefix, self.config)
418 }
419}
420
421impl<T: FormModel> Default for ModelFormSetBuilder<T> {
422 fn default() -> Self {
423 Self::new()
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430 use serde_json::Value;
431
432 struct Article {
434 id: i32,
435 title: String,
436 content: String,
437 }
438
439 impl FormModel for Article {
440 fn field_names() -> Vec<String> {
441 vec!["id".to_string(), "title".to_string(), "content".to_string()]
442 }
443
444 fn get_field(&self, name: &str) -> Option<Value> {
445 match name {
446 "id" => Some(Value::Number(self.id.into())),
447 "title" => Some(Value::String(self.title.clone())),
448 "content" => Some(Value::String(self.content.clone())),
449 _ => None,
450 }
451 }
452
453 fn set_field(&mut self, name: &str, value: Value) -> Result<(), String> {
454 match name {
455 "id" => {
456 if let Value::Number(n) = value {
457 self.id = n.as_i64().unwrap() as i32;
458 Ok(())
459 } else {
460 Err("Invalid type for id".to_string())
461 }
462 }
463 "title" => {
464 if let Value::String(s) = value {
465 self.title = s;
466 Ok(())
467 } else {
468 Err("Invalid type for title".to_string())
469 }
470 }
471 "content" => {
472 if let Value::String(s) = value {
473 self.content = s;
474 Ok(())
475 } else {
476 Err("Invalid type for content".to_string())
477 }
478 }
479 _ => Err(format!("Unknown field: {}", name)),
480 }
481 }
482
483 fn save(&mut self) -> Result<(), String> {
484 Ok(())
486 }
487 }
488
489 #[test]
490 fn test_model_formset_config() {
491 let config = ModelFormSetConfig::new()
492 .with_extra(3)
493 .with_can_delete(true)
494 .with_max_num(Some(10))
495 .with_min_num(1);
496
497 assert_eq!(config.extra, 3);
498 assert!(config.can_delete);
499 assert_eq!(config.max_num, Some(10));
500 assert_eq!(config.min_num, 1);
501 }
502
503 #[test]
504 fn test_model_formset_empty() {
505 let config = ModelFormSetConfig::new().with_extra(2);
506 let formset = ModelFormSet::<Article>::empty("article".to_string(), config);
507
508 assert_eq!(formset.prefix(), "article");
509 assert_eq!(formset.instances().len(), 0);
510 assert_eq!(formset.total_form_count(), 2);
511 }
512
513 #[test]
514 fn test_model_formset_with_instances() {
515 let instances = vec![
516 Article {
517 id: 1,
518 title: "First Article".to_string(),
519 content: "Content 1".to_string(),
520 },
521 Article {
522 id: 2,
523 title: "Second Article".to_string(),
524 content: "Content 2".to_string(),
525 },
526 ];
527
528 let config = ModelFormSetConfig::new();
529 let formset = ModelFormSet::new("article".to_string(), instances, config);
530
531 assert_eq!(formset.instances().len(), 2);
532 assert_eq!(formset.form_count(), 2);
533 }
534
535 #[test]
536 fn test_model_formset_builder() {
537 let formset = ModelFormSetBuilder::<Article>::new()
538 .extra(3)
539 .can_delete(true)
540 .max_num(5)
541 .build_empty("article".to_string());
542
543 assert_eq!(formset.total_form_count(), 3);
544 }
545
546 #[test]
547 fn test_model_formset_management_data() {
548 let config = ModelFormSetConfig::new().with_extra(2).with_min_num(1);
549 let formset = ModelFormSet::<Article>::empty("article".to_string(), config);
550
551 let mgmt_data = formset.management_form_data();
552
553 assert_eq!(mgmt_data.get("article-TOTAL_FORMS"), Some(&"2".to_string()));
554 assert_eq!(
555 mgmt_data.get("article-INITIAL_FORMS"),
556 Some(&"0".to_string())
557 );
558 assert_eq!(
559 mgmt_data.get("article-MIN_NUM_FORMS"),
560 Some(&"1".to_string())
561 );
562 }
563}