Skip to content

Commit fc68a5f

Browse files
committed
Error chain
1 parent 676da77 commit fc68a5f

File tree

5 files changed

+214
-42
lines changed

5 files changed

+214
-42
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Don't show `Cargo.lock` in `git diff`
2+
Cargo.lock -diff

Cargo.lock

+151-23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ thiserror = "1"
2020
[dev-dependencies]
2121
anyhow = "1"
2222
derive_more = "0.99"
23-
env_logger = "0.9"
23+
env_logger = "0.10"
2424
rand = "0.8"
2525
rusty-hook = "0.11"

src/lib.rs

+55-13
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ use store::Store;
7676

7777
mod store;
7878

79-
type StdResult<T, E> = std::result::Result<T, E>;
80-
8179
#[derive(thiserror::Error, Debug)]
8280
pub enum Error {
8381
#[error("IO error: {0}")]
@@ -88,14 +86,12 @@ pub enum Error {
8886
Step(String),
8987
}
9088

91-
pub type Result<T> = StdResult<T, Error>;
92-
9389
/// 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>>;
9692

9793
/// 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>;
9995
}
10096

10197
/// Machine state with metadata to store
@@ -124,7 +120,7 @@ where
124120
M: fmt::Debug + Serialize + DeserializeOwned + State<M>,
125121
{
126122
/// Creates an Engine using initial state
127-
pub fn new(state: M) -> Result<Self> {
123+
pub fn new(state: M) -> Result<Self, Error> {
128124
let store: Store = Store::new()?;
129125
let step = Step::new(state);
130126
Ok(Self { store, step })
@@ -138,15 +134,15 @@ where
138134
}
139135

140136
/// Restores an Engine from the previous run
141-
pub fn restore(mut self) -> Result<Self> {
137+
pub fn restore(mut self) -> Result<Self, Error> {
142138
if let Some(step) = self.store.load()? {
143139
self.step = step;
144140
}
145141
Ok(self)
146142
}
147143

148144
/// Runs all steps to completion
149-
pub fn run(mut self) -> Result<()> {
145+
pub fn run(mut self) -> Result<(), Error> {
150146
if let Some(e) = self.step.error.as_ref() {
151147
return Err(crate::Error::Step(format!(
152148
"Previous run resulted in an error: {} on step: {:?}",
@@ -170,7 +166,7 @@ where
170166
}
171167
Err(e) => {
172168
self.step.state = serde_json::from_str(&state_backup)?;
173-
let err_str = format!("{:?}", e);
169+
let err_str = error_chain(&*e.into());
174170
self.step.error = Some(err_str.clone());
175171
self.save()?;
176172
return Err(crate::Error::Step(err_str));
@@ -181,13 +177,59 @@ where
181177
}
182178

183179
/// Drops the previous error
184-
pub fn drop_error(&mut self) -> Result<()> {
180+
pub fn drop_error(&mut self) -> Result<(), Error> {
185181
self.step.error = None;
186182
self.save()
187183
}
188184

189-
fn save(&self) -> Result<()> {
185+
fn save(&self) -> Result<(), Error> {
190186
self.store.save(&self.step)?;
191187
Ok(())
192188
}
193189
}
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("\nCaused 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

Comments
 (0)