Skip to main content

component_map/
sync_fallible.rs

1use crate::{ComponentManager, Keyed, WithArgs};
2
3impl<Key, Args, Comp, FnInit> ComponentManager<Key, Args, Comp, FnInit> {
4    pub fn try_init<Error>(
5        args: impl IntoIterator<Item = (Key, Args)>,
6        init: FnInit,
7    ) -> Result<Self, Error>
8    where
9        Key: Eq + std::hash::Hash,
10        FnInit: Fn(&Args) -> Result<Comp, Error>,
11    {
12        let map = args
13            .into_iter()
14            .map(|(key, args)| {
15                let component = (init)(&args)?;
16                Ok((key, WithArgs { component, args }))
17            })
18            .collect::<Result<_, _>>()?;
19
20        Ok(Self { map, init })
21    }
22
23    pub fn try_reinit_all<Error>(
24        &mut self,
25    ) -> impl Iterator<Item = Keyed<&Key, Result<Comp, Error>>>
26    where
27        FnInit: Fn(&Args) -> Result<Comp, Error>,
28    {
29        self.map.iter_mut().map(|(key, component)| {
30            let result = (self.init)(&component.args)
31                .map(|next| std::mem::replace(&mut component.component, next));
32
33            Keyed::new(key, result)
34        })
35    }
36
37    pub fn try_reinit<Error>(
38        &mut self,
39        keys: impl IntoIterator<Item = Key>,
40    ) -> impl Iterator<Item = Keyed<Key, Option<Result<Comp, Error>>>>
41    where
42        Key: Eq + std::hash::Hash,
43        FnInit: Fn(&Args) -> Result<Comp, Error>,
44    {
45        keys.into_iter().map(|key| {
46            let prev = self.map.get_mut(&key).map(|component| {
47                (self.init)(&component.args)
48                    .map(|next| std::mem::replace(&mut component.component, next))
49            });
50
51            Keyed::new(key, prev)
52        })
53    }
54
55    pub fn try_update<Error>(
56        &mut self,
57        updates: impl IntoIterator<Item = (Key, Args)>,
58    ) -> impl Iterator<Item = Keyed<Key, Option<Result<WithArgs<Args, Comp>, Error>>>>
59    where
60        Key: Clone + Eq + std::hash::Hash,
61        FnInit: Fn(&Args) -> Result<Comp, Error>,
62    {
63        updates.into_iter().map(move |(key, args)| {
64            let result = (self.init)(&args)
65                .map(|component| self.map.insert(key.clone(), WithArgs { component, args }));
66
67            Keyed::new(key, result.transpose())
68        })
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use std::sync::{Arc, Mutex};
76
77    #[derive(Debug, Clone, PartialEq, Eq)]
78    struct Counter(usize);
79
80    #[derive(Debug, Clone, PartialEq, Eq)]
81    struct FailArgs {
82        value: usize,
83        should_fail: bool,
84    }
85
86    #[derive(Debug, PartialEq, Eq)]
87    struct TestError(String);
88
89    #[test]
90    fn test_try_init_success() {
91        let init = |args: &FailArgs| -> Result<Counter, TestError> {
92            if args.should_fail {
93                Err(TestError("Failed".to_string()))
94            } else {
95                Ok(Counter(args.value))
96            }
97        };
98
99        let result = ComponentManager::try_init(
100            [
101                (
102                    "key1",
103                    FailArgs {
104                        value: 1,
105                        should_fail: false,
106                    },
107                ),
108                (
109                    "key2",
110                    FailArgs {
111                        value: 2,
112                        should_fail: false,
113                    },
114                ),
115            ],
116            init,
117        );
118
119        assert!(result.is_ok());
120        let manager = result.unwrap();
121        assert_eq!(manager.components().len(), 2);
122        assert_eq!(
123            manager.components().get("key1").unwrap().component,
124            Counter(1)
125        );
126        assert_eq!(
127            manager.components().get("key2").unwrap().component,
128            Counter(2)
129        );
130    }
131
132    #[test]
133    fn test_try_init_failure() {
134        let init = |args: &FailArgs| -> Result<Counter, TestError> {
135            if args.should_fail {
136                Err(TestError("Failed".to_string()))
137            } else {
138                Ok(Counter(args.value))
139            }
140        };
141
142        let result = ComponentManager::try_init(
143            [
144                (
145                    "key1",
146                    FailArgs {
147                        value: 1,
148                        should_fail: false,
149                    },
150                ),
151                (
152                    "key2",
153                    FailArgs {
154                        value: 2,
155                        should_fail: true,
156                    },
157                ),
158            ],
159            init,
160        );
161
162        assert!(result.is_err());
163        assert_eq!(result.err().unwrap(), TestError("Failed".to_string()));
164    }
165
166    #[test]
167    fn test_try_init_empty() {
168        let init = |args: &FailArgs| -> Result<Counter, TestError> {
169            if args.should_fail {
170                Err(TestError("Failed".to_string()))
171            } else {
172                Ok(Counter(args.value))
173            }
174        };
175
176        let result: Result<ComponentManager<&str, FailArgs, Counter, _>, TestError> =
177            ComponentManager::try_init([], init);
178
179        assert!(result.is_ok());
180        assert_eq!(result.unwrap().components().len(), 0);
181    }
182
183    #[test]
184    fn test_try_init_all_fail() {
185        let init = |args: &FailArgs| -> Result<Counter, TestError> {
186            if args.should_fail {
187                Err(TestError("Failed".to_string()))
188            } else {
189                Ok(Counter(args.value))
190            }
191        };
192
193        let result = ComponentManager::try_init(
194            [
195                (
196                    "key1",
197                    FailArgs {
198                        value: 1,
199                        should_fail: true,
200                    },
201                ),
202                (
203                    "key2",
204                    FailArgs {
205                        value: 2,
206                        should_fail: true,
207                    },
208                ),
209            ],
210            init,
211        );
212
213        assert!(result.is_err());
214    }
215
216    #[test]
217    fn test_try_reinit_all_success() {
218        let init = |args: &FailArgs| -> Result<Counter, TestError> {
219            if args.should_fail {
220                Err(TestError("Failed".to_string()))
221            } else {
222                Ok(Counter(args.value * 2))
223            }
224        };
225
226        let mut manager = ComponentManager::try_init(
227            [
228                (
229                    "key1",
230                    FailArgs {
231                        value: 1,
232                        should_fail: false,
233                    },
234                ),
235                (
236                    "key2",
237                    FailArgs {
238                        value: 2,
239                        should_fail: false,
240                    },
241                ),
242            ],
243            init,
244        )
245        .unwrap();
246
247        let results: Vec<_> = manager.try_reinit_all().collect();
248
249        assert_eq!(results.len(), 2);
250        assert!(results.iter().all(|r| r.value.is_ok()));
251
252        // Check that components are updated
253        assert_eq!(
254            manager.components().get("key1").unwrap().component,
255            Counter(2)
256        );
257        assert_eq!(
258            manager.components().get("key2").unwrap().component,
259            Counter(4)
260        );
261    }
262
263    #[test]
264    fn test_try_reinit_all_with_failure() {
265        let call_count = Arc::new(Mutex::new(0));
266        let call_count_clone = call_count.clone();
267
268        let init = move |args: &FailArgs| -> Result<Counter, TestError> {
269            let count = *call_count_clone.lock().unwrap();
270            *call_count_clone.lock().unwrap() += 1;
271
272            // Fail on reinit (after initial successful init)
273            if count >= 2 && args.should_fail {
274                Err(TestError("Failed on reinit".to_string()))
275            } else {
276                Ok(Counter(args.value * 2))
277            }
278        };
279
280        let mut manager = ComponentManager::try_init(
281            [
282                (
283                    "key1",
284                    FailArgs {
285                        value: 1,
286                        should_fail: false,
287                    },
288                ),
289                (
290                    "key2",
291                    FailArgs {
292                        value: 2,
293                        should_fail: true,
294                    },
295                ),
296            ],
297            init,
298        )
299        .unwrap();
300
301        let results: Vec<_> = manager.try_reinit_all().collect();
302
303        assert_eq!(results.len(), 2);
304        let failures: Vec<_> = results.iter().filter(|r| r.value.is_err()).collect();
305        assert_eq!(failures.len(), 1);
306        let successes: Vec<_> = results.iter().filter(|r| r.value.is_ok()).collect();
307        assert_eq!(successes.len(), 1);
308    }
309
310    #[test]
311    fn test_try_reinit_all_preserves_on_error() {
312        let init = |args: &FailArgs| -> Result<Counter, TestError> {
313            if args.should_fail {
314                Err(TestError("Failed".to_string()))
315            } else {
316                Ok(Counter(args.value * 2))
317            }
318        };
319
320        let mut manager = ComponentManager::try_init(
321            [(
322                "key1",
323                FailArgs {
324                    value: 1,
325                    should_fail: false,
326                },
327            )],
328            init,
329        )
330        .unwrap();
331
332        // Change args to make it fail
333        manager
334            .components_mut()
335            .get_mut("key1")
336            .unwrap()
337            .args
338            .should_fail = true;
339
340        let original_value = manager.components().get("key1").unwrap().component.clone();
341        let _results: Vec<_> = manager.try_reinit_all().collect();
342
343        // Component should remain unchanged on error
344        assert_eq!(
345            manager.components().get("key1").unwrap().component,
346            original_value
347        );
348    }
349
350    #[test]
351    fn test_try_reinit_specific_keys_success() {
352        let init = |args: &FailArgs| -> Result<Counter, TestError> {
353            if args.should_fail {
354                Err(TestError("Failed".to_string()))
355            } else {
356                Ok(Counter(args.value * 3))
357            }
358        };
359
360        let mut manager = ComponentManager::try_init(
361            [
362                (
363                    "key1",
364                    FailArgs {
365                        value: 1,
366                        should_fail: false,
367                    },
368                ),
369                (
370                    "key2",
371                    FailArgs {
372                        value: 2,
373                        should_fail: false,
374                    },
375                ),
376            ],
377            init,
378        )
379        .unwrap();
380
381        let results: Vec<_> = manager.try_reinit(["key1"]).collect();
382
383        assert_eq!(results.len(), 1);
384        assert!(results[0].value.as_ref().unwrap().is_ok());
385        assert_eq!(
386            manager.components().get("key1").unwrap().component,
387            Counter(3)
388        );
389        // key2 should be unchanged from initial
390        assert_eq!(
391            manager.components().get("key2").unwrap().component,
392            Counter(6)
393        );
394    }
395
396    #[test]
397    fn test_try_reinit_nonexistent_key() {
398        let init = |args: &FailArgs| -> Result<Counter, TestError> {
399            if args.should_fail {
400                Err(TestError("Failed".to_string()))
401            } else {
402                Ok(Counter(args.value))
403            }
404        };
405
406        let mut manager = ComponentManager::try_init(
407            [(
408                "key1",
409                FailArgs {
410                    value: 1,
411                    should_fail: false,
412                },
413            )],
414            init,
415        )
416        .unwrap();
417
418        let results: Vec<_> = manager.try_reinit(["nonexistent"]).collect();
419
420        assert_eq!(results.len(), 1);
421        assert_eq!(results[0].key, "nonexistent");
422        assert!(results[0].value.is_none());
423    }
424
425    #[test]
426    fn test_try_reinit_with_failure() {
427        let init = |args: &FailArgs| -> Result<Counter, TestError> {
428            if args.should_fail {
429                Err(TestError("Failed".to_string()))
430            } else {
431                Ok(Counter(args.value))
432            }
433        };
434
435        let mut manager = ComponentManager::try_init(
436            [(
437                "key1",
438                FailArgs {
439                    value: 1,
440                    should_fail: false,
441                },
442            )],
443            init,
444        )
445        .unwrap();
446
447        // Set to fail
448        manager
449            .components_mut()
450            .get_mut("key1")
451            .unwrap()
452            .args
453            .should_fail = true;
454
455        let results: Vec<_> = manager.try_reinit(["key1"]).collect();
456
457        assert_eq!(results.len(), 1);
458        assert!(results[0].value.as_ref().unwrap().is_err());
459    }
460
461    #[test]
462    fn test_try_update_new_key_success() {
463        let init = |args: &FailArgs| -> Result<Counter, TestError> {
464            if args.should_fail {
465                Err(TestError("Failed".to_string()))
466            } else {
467                Ok(Counter(args.value))
468            }
469        };
470
471        let mut manager = ComponentManager::try_init(
472            [(
473                "key1",
474                FailArgs {
475                    value: 1,
476                    should_fail: false,
477                },
478            )],
479            init,
480        )
481        .unwrap();
482
483        let results: Vec<_> = manager
484            .try_update([(
485                "key2",
486                FailArgs {
487                    value: 20,
488                    should_fail: false,
489                },
490            )])
491            .collect();
492
493        assert_eq!(results.len(), 1);
494        assert!(results[0].value.is_none());
495        assert_eq!(manager.components().len(), 2);
496        assert_eq!(
497            manager.components().get("key2").unwrap().component,
498            Counter(20)
499        );
500    }
501
502    #[test]
503    fn test_try_update_existing_key_success() {
504        let init = |args: &FailArgs| -> Result<Counter, TestError> {
505            if args.should_fail {
506                Err(TestError("Failed".to_string()))
507            } else {
508                Ok(Counter(args.value))
509            }
510        };
511
512        let mut manager = ComponentManager::try_init(
513            [(
514                "key1",
515                FailArgs {
516                    value: 1,
517                    should_fail: false,
518                },
519            )],
520            init,
521        )
522        .unwrap();
523
524        let results: Vec<_> = manager
525            .try_update([(
526                "key1",
527                FailArgs {
528                    value: 10,
529                    should_fail: false,
530                },
531            )])
532            .collect();
533
534        assert_eq!(results.len(), 1);
535        assert!(results[0].value.is_some());
536        let prev = results[0].value.as_ref().unwrap().as_ref().unwrap();
537        assert_eq!(prev.component, Counter(1));
538
539        assert_eq!(
540            manager.components().get("key1").unwrap().component,
541            Counter(10)
542        );
543    }
544
545    #[test]
546    fn test_try_update_failure() {
547        let init = |args: &FailArgs| -> Result<Counter, TestError> {
548            if args.should_fail {
549                Err(TestError("Failed".to_string()))
550            } else {
551                Ok(Counter(args.value))
552            }
553        };
554
555        let mut manager = ComponentManager::try_init(
556            [(
557                "key1",
558                FailArgs {
559                    value: 1,
560                    should_fail: false,
561                },
562            )],
563            init,
564        )
565        .unwrap();
566
567        let results: Vec<_> = manager
568            .try_update([(
569                "key2",
570                FailArgs {
571                    value: 20,
572                    should_fail: true,
573                },
574            )])
575            .collect();
576
577        assert_eq!(results.len(), 1);
578        assert!(results[0].value.is_some());
579        assert!(results[0].value.as_ref().unwrap().is_err());
580
581        // Should not insert on error
582        assert_eq!(manager.components().len(), 1);
583        assert!(manager.components().get("key2").is_none());
584    }
585
586    #[test]
587    fn test_try_update_multiple_mixed() {
588        let init = |args: &FailArgs| -> Result<Counter, TestError> {
589            if args.should_fail {
590                Err(TestError("Failed".to_string()))
591            } else {
592                Ok(Counter(args.value))
593            }
594        };
595
596        let mut manager = ComponentManager::try_init(
597            [(
598                "key1",
599                FailArgs {
600                    value: 1,
601                    should_fail: false,
602                },
603            )],
604            init,
605        )
606        .unwrap();
607
608        let results: Vec<_> = manager
609            .try_update([
610                (
611                    "key2",
612                    FailArgs {
613                        value: 20,
614                        should_fail: false,
615                    },
616                ),
617                (
618                    "key3",
619                    FailArgs {
620                        value: 30,
621                        should_fail: true,
622                    },
623                ),
624                (
625                    "key4",
626                    FailArgs {
627                        value: 40,
628                        should_fail: false,
629                    },
630                ),
631            ])
632            .collect();
633
634        assert_eq!(results.len(), 3);
635
636        // Check that only successful updates were inserted
637        assert_eq!(manager.components().len(), 3); // key1, key2, key4
638        assert!(manager.components().get("key2").is_some());
639        assert!(manager.components().get("key3").is_none());
640        assert!(manager.components().get("key4").is_some());
641    }
642}