1use crate::{Error, Result, StdError};
2use std::any::TypeId;
3
4pub trait Wrapper<T, E> {
6 fn pass(self) -> Result<T>;
9
10 fn wrap(self, msg: &str) -> Result<T>;
12
13 fn err_is<U>(&self) -> bool
15 where
16 U: StdError+'static;
17
18 fn retry<F>(self, max: usize, f: F) -> Result<T, E>
20 where
21 F: Fn(usize) -> Result<T, E>;
22
23 fn retry_on<F>(self, max: usize, id: TypeId, f: F) -> Result<T, E>
25 where
26 F: Fn(usize) -> Result<T, E>;
27}
28
29impl<T, E> Wrapper<T, E> for Result<T, E>
30where
31 E: StdError+Send+Sync+'static,
32{
33 fn pass(self) -> Result<T> {
34 match self {
35 Err(err) => Error::pass(err),
36 Ok(val) => Ok(val),
37 }
38 }
39
40 fn wrap(self, msg: &str) -> Result<T> {
41 match self {
42 Err(err) => Error::wrap(err, msg),
43 Ok(val) => Ok(val),
44 }
45 }
46
47 fn err_is<U>(&self) -> bool
48 where
49 U: StdError+'static,
50 {
51 match self {
52 Ok(_) => false,
53 Err(e) => (e as &(dyn StdError+'static)).is::<U>(),
54 }
55 }
56
57 fn retry<F>(self, max: usize, f: F) -> Result<T, E>
58 where
59 F: Fn(usize) -> Result<T, E>,
60 {
61 let mut retries = 0;
62 let mut result = self;
63 while retries < max && result.is_err() {
64 retries += 1;
65 result = f(retries);
66 }
67 result
68 }
69
70 fn retry_on<F>(self, max: usize, id: TypeId, f: F) -> Result<T, E>
71 where
72 F: Fn(usize) -> Result<T, E>,
73 {
74 let mut retries = 0;
75 let mut result = self;
76 while retries < max
77 && match result {
78 Ok(_) => false,
79 Err(_) => TypeId::of::<E>() == id,
80 }
81 {
82 retries += 1;
83 result = f(retries);
84 }
85 result
86 }
87}
88
89#[cfg(test)]
92mod tests {
93 use super::*;
94
95 use std::sync::Once;
96 static INIT: Once = Once::new();
97 pub fn initialize() {
98 INIT.call_once(|| {
99 std::env::set_var(gory::TERM_COLOR, "0");
100 std::env::set_var("RUST_BACKTRACE", "0");
101 });
102 }
103
104 fn pass() -> Result<()> {
105 do_external_thing().pass()
106 }
107
108 fn retry() -> Result<()> {
109 do_external_thing().retry(3, |_| do_external_thing()).wrap("Failed while attacking beast")
110 }
111
112 fn retry_on_concreate_error_type_using_err_is() -> Result<()> {
113 let mut retries = 0;
114 let mut result = do_external_thing();
115 while retries < 3 && result.err_is::<std::io::Error>() {
116 retries += 1;
117 result = do_external_thing();
118 }
119 result.wrap(&format!("Failed while attacking beast: {}", retries))
120 }
121
122 fn retry_on_concreate_error_type() -> Result<()> {
123 do_external_thing().retry_on(3, TypeId::of::<std::io::Error>(), |_| do_external_thing()).wrap("Failed while attacking beast")
124 }
125
126 fn do_external_thing() -> std::io::Result<()> {
127 Err(std::io::Error::new(std::io::ErrorKind::Other, "Oh no, we missed!"))
128 }
129
130 #[test]
131 fn test_pass() {
132 initialize();
133 let err = pass().unwrap_err();
134 assert_eq!("Oh no, we missed!", err.to_string());
135
136 assert!(!err.is::<Error>());
137 assert!(err.is::<std::io::Error>());
138 assert!(err.downcast_ref::<Error>().is_none());
139 assert!(err.downcast_ref::<std::io::Error>().is_some());
140 }
141
142 #[test]
143 fn test_retry_on() {
144 initialize();
145 assert_eq!("Failed while attacking beast", retry().unwrap_err().to_string());
146 assert_eq!("Failed while attacking beast", retry_on_concreate_error_type().unwrap_err().to_string());
147 assert_eq!("Failed while attacking beast: 3", retry_on_concreate_error_type_using_err_is().unwrap_err().to_string());
148 }
149}