1pub trait FallbackIf<R> {
3 fn fallback_if<P, F>(self, predicate: P, alternative: F) -> R
6 where
7 P: Into<bool>,
8 F: FnOnce() -> R;
9}
10
11impl<T, E> FallbackIf<Result<T, E>> for Result<T, E> {
12 fn fallback_if<P, F>(self, predicate: P, alternative: F) -> Result<T, E>
15 where
16 P: Into<bool>,
17 F: FnOnce() -> Result<T, E>,
18 {
19 if self.is_err() && predicate.into() {
20 alternative()
21 } else {
22 self
23 }
24 }
25}
26impl<T> FallbackIf<Option<T>> for Option<T> {
27 fn fallback_if<P, F>(self, predicate: P, alternative: F) -> Option<T>
28 where
29 P: Into<bool>,
30 F: FnOnce() -> Option<T>,
31 {
32 if self.is_none() && predicate.into() {
33 alternative()
34 } else {
35 self
36 }
37 }
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[yare::parameterized(
45 ok = { Ok(1), Ok(9), Ok(1) }, err_to_ok = { Err(()), Ok(9), Ok(9) }, err_to_err = { Err(()), Err(()), Err(()) }, )]
49 fn result_do_fallback(
50 result: Result<u32, ()>,
51 fallback_result: Result<u32, ()>,
52 expected: Result<u32, ()>,
53 ) {
54 let outcome = result.fallback_if(true, || fallback_result);
55
56 assert_eq!(outcome, expected)
57 }
58
59 #[yare::parameterized(
60 ok = { Ok(1), Ok(9), Ok(1) }, err_to_ok = { Err(()), Ok(9), Err(()) }, err_to_err = { Err(()), Err(()), Err(()) }, )]
64 fn result_do_not_fallback(
65 result: Result<u32, ()>,
66 fallback_result: Result<u32, ()>,
67 expected: Result<u32, ()>,
68 ) {
69 let outcome = result.fallback_if(false, || fallback_result);
70
71 assert_eq!(outcome, expected)
72 }
73
74 #[yare::parameterized(
75 ok = { Some(1), Some(9), Some(1) }, none_to_some = { None, Some(9), Some(9) }, none_to_none = { None, None, None }, )]
79 fn option_do_fallback(result: Option<u32>, fallback_value: Option<u32>, expected: Option<u32>) {
80 let outcome = result.fallback_if(true, || fallback_value);
81
82 assert_eq!(outcome, expected)
83 }
84
85 #[yare::parameterized(
86 ok = { Some(1), Some(9), Some(1) }, none_to_some = { None, Some(9), None }, none_to_none = { None, None, None }, )]
90 fn option_do_not_fallback(
91 result: Option<u32>,
92 fallback_value: Option<u32>,
93 expected: Option<u32>,
94 ) {
95 let outcome = result.fallback_if(false, || fallback_value);
96
97 assert_eq!(outcome, expected)
98 }
99
100 #[test]
101 fn readme() {
102 struct Config {
103 fallback_to_local: bool,
104 }
105
106 #[derive(Debug, PartialEq)]
107 struct Manifest;
108
109 impl Manifest {
110 pub fn try_fetch_remote() -> Result<Self, ()> {
111 Err(())
113 }
114
115 pub fn try_fetch_local() -> Result<Self, ()> {
116 Ok(Manifest)
118 }
119 }
120
121 let config = Config {
122 fallback_to_local: true,
123 };
124 let result = Manifest::try_fetch_remote();
125
126 let outcome = result.fallback_if(config.fallback_to_local, || Manifest::try_fetch_local());
127
128 assert_eq!(outcome, Ok(Manifest))
129 }
130}