Skip to main content

component_map/
sync_fallible.rs

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