@@ -76,8 +76,6 @@ use store::Store;
76
76
77
77
mod store;
78
78
79
- type StdResult < T , E > = std:: result:: Result < T , E > ;
80
-
81
79
#[ derive( thiserror:: Error , Debug ) ]
82
80
pub enum Error {
83
81
#[ error( "IO error: {0}" ) ]
@@ -88,14 +86,12 @@ pub enum Error {
88
86
Step ( String ) ,
89
87
}
90
88
91
- pub type Result < T > = StdResult < T , Error > ;
92
-
93
89
/// Represents state of a state machine M
94
- pub trait State < M : State < M > > {
95
- type Error : fmt :: Debug ;
90
+ pub trait State < M > {
91
+ type Error : Into < Box < dyn std :: error :: Error + ' static > > ;
96
92
97
93
/// Runs the current step and returns the next machine state or `None` if everything is done
98
- fn next ( self ) -> StdResult < Option < M > , Self :: Error > ;
94
+ fn next ( self ) -> Result < Option < M > , Self :: Error > ;
99
95
}
100
96
101
97
/// Machine state with metadata to store
@@ -124,7 +120,7 @@ where
124
120
M : fmt:: Debug + Serialize + DeserializeOwned + State < M > ,
125
121
{
126
122
/// Creates an Engine using initial state
127
- pub fn new ( state : M ) -> Result < Self > {
123
+ pub fn new ( state : M ) -> Result < Self , Error > {
128
124
let store: Store = Store :: new ( ) ?;
129
125
let step = Step :: new ( state) ;
130
126
Ok ( Self { store, step } )
@@ -138,15 +134,15 @@ where
138
134
}
139
135
140
136
/// Restores an Engine from the previous run
141
- pub fn restore ( mut self ) -> Result < Self > {
137
+ pub fn restore ( mut self ) -> Result < Self , Error > {
142
138
if let Some ( step) = self . store . load ( ) ? {
143
139
self . step = step;
144
140
}
145
141
Ok ( self )
146
142
}
147
143
148
144
/// Runs all steps to completion
149
- pub fn run ( mut self ) -> Result < ( ) > {
145
+ pub fn run ( mut self ) -> Result < ( ) , Error > {
150
146
if let Some ( e) = self . step . error . as_ref ( ) {
151
147
return Err ( crate :: Error :: Step ( format ! (
152
148
"Previous run resulted in an error: {} on step: {:?}" ,
@@ -170,7 +166,7 @@ where
170
166
}
171
167
Err ( e) => {
172
168
self . step . state = serde_json:: from_str ( & state_backup) ?;
173
- let err_str = format ! ( "{:?}" , e ) ;
169
+ let err_str = error_chain ( & * e . into ( ) ) ;
174
170
self . step . error = Some ( err_str. clone ( ) ) ;
175
171
self . save ( ) ?;
176
172
return Err ( crate :: Error :: Step ( err_str) ) ;
@@ -181,13 +177,59 @@ where
181
177
}
182
178
183
179
/// Drops the previous error
184
- pub fn drop_error ( & mut self ) -> Result < ( ) > {
180
+ pub fn drop_error ( & mut self ) -> Result < ( ) , Error > {
185
181
self . step . error = None ;
186
182
self . save ( )
187
183
}
188
184
189
- fn save ( & self ) -> Result < ( ) > {
185
+ fn save ( & self ) -> Result < ( ) , Error > {
190
186
self . store . save ( & self . step ) ?;
191
187
Ok ( ( ) )
192
188
}
193
189
}
190
+
191
+ /// A helper function to format an error with its source chain.
192
+ ///
193
+ /// This function works with both `&Error` and `Box<dyn Error>`. When passing a boxed error,
194
+ /// make sure to dereference it using `&*e`.
195
+ pub fn error_chain ( e : & ( dyn std:: error:: Error + ' static ) ) -> String {
196
+ use std:: fmt:: Write as _;
197
+
198
+ let mut s = e. to_string ( ) ;
199
+ let mut current = e. source ( ) ;
200
+ if current. is_some ( ) {
201
+ s. push_str ( "\n Caused by:" ) ;
202
+ }
203
+ while let Some ( cause) = current {
204
+ write ! ( s, "\n \t {}" , cause) . ok ( ) ;
205
+ current = cause. source ( ) ;
206
+ }
207
+ s
208
+ }
209
+
210
+ #[ cfg( test) ]
211
+ mod tests {
212
+ use super :: * ;
213
+
214
+ #[ test]
215
+ fn both_stderror_and_anyhow_work ( ) {
216
+ struct Foo ;
217
+ struct Bar ;
218
+
219
+ impl State < ( ) > for Foo {
220
+ type Error = anyhow:: Error ;
221
+
222
+ fn next ( self ) -> Result < Option < ( ) > , Self :: Error > {
223
+ todo ! ( )
224
+ }
225
+ }
226
+
227
+ impl State < ( ) > for Bar {
228
+ type Error = std:: io:: Error ;
229
+
230
+ fn next ( self ) -> Result < Option < ( ) > , Self :: Error > {
231
+ todo ! ( )
232
+ }
233
+ }
234
+ }
235
+ }
0 commit comments