Skip to content

Commit 287fec9

Browse files
committed
Refactor error handling
This change organizes error types to distinguish errors that can happen at different stages of the worker lifecycle. This should make it easier for users to identify what caused a `Worker::seek()` operation to fail Change-type: patch
1 parent be76706 commit 287fec9

19 files changed

Lines changed: 258 additions & 213 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ serde = { version = "1.0.197" }
1818
serde_json = "1.0.120"
1919
thiserror = "2"
2020
tokio = { version = "1.43.0", default-features = false, features = [
21+
"rt",
2122
"sync",
2223
"macros",
2324
] }

examples/composer/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ mod tests {
776776

777777
// Seeking the target must succeed
778778
let worker = worker.seek_target(target.clone()).await.unwrap();
779-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
779+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
780780

781781
// The alpine image must exist now
782782
let docker = Docker::connect_with_defaults().unwrap();
@@ -934,7 +934,7 @@ mod tests {
934934

935935
// Seeking the target must succeed
936936
let worker = worker.seek_target(target).await.unwrap();
937-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
937+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
938938

939939
// The alpine image must exist now
940940
let docker = Docker::connect_with_defaults().unwrap();
@@ -973,7 +973,7 @@ mod tests {
973973

974974
// Seeking the target must succeed
975975
let worker = worker.seek_target(target).await.unwrap();
976-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
976+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
977977

978978
// The container ids should match
979979
let container = docker
@@ -1000,7 +1000,7 @@ mod tests {
10001000

10011001
// Seeking the target must succeed
10021002
let worker = worker.seek_target(target).await.unwrap();
1003-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
1003+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
10041004

10051005
// The container ids should match
10061006
let container = docker
@@ -1021,7 +1021,7 @@ mod tests {
10211021

10221022
// Seeking the target must succeed
10231023
let worker = worker.seek_target(target).await.unwrap();
1024-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
1024+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
10251025

10261026
// The container should no longer exist
10271027
let container = docker

src/errors.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::ops::Deref;
2+
3+
use thiserror::Error;
4+
5+
#[derive(Debug, Error)]
6+
#[error("argument extraction failed: {0:?}")]
7+
/// Arguments to a task could not be extracted.
8+
/// This is likely to be an error with the task definition.
9+
pub struct ExtractionError(#[from] anyhow::Error);
10+
11+
#[derive(Debug, Error)]
12+
#[error("serialization error: {0:?}")]
13+
/// An error happened while serializing or deserializing an input type
14+
pub struct SerializationError(#[from] serde_json::Error);
15+
16+
#[derive(Debug, Error)]
17+
#[error("internal error, this may be a bug: {0:?}")]
18+
/// Some unexpected error happened during the worker operation, either
19+
/// planning or runtime. These errors should not happen, unless
20+
/// there is a bug in the implementation.
21+
pub struct InternalError(#[from] anyhow::Error);
22+
23+
#[derive(Debug, Error)]
24+
#[error("method expansion failed: {0:?}")]
25+
/// An error happened while trying to expand the method
26+
/// task into its sub-tasks. This is likely an issue with the
27+
/// method definition
28+
pub struct MethodError(Box<dyn std::error::Error + Send + Sync>);
29+
30+
impl MethodError {
31+
pub fn new<E: std::error::Error + Send + Sync + 'static>(err: E) -> Self {
32+
Self(Box::new(err))
33+
}
34+
}
35+
36+
#[derive(Debug, Error)]
37+
#[error(transparent)]
38+
/// An error happened while executing the task within the workflow.
39+
/// These errors only happen at runtime, never at the planning stage
40+
/// of the worker.
41+
pub struct IOError(Box<dyn std::error::Error + Send + Sync>);
42+
43+
impl IOError {
44+
pub fn new<E: std::error::Error + Send + Sync + 'static>(err: E) -> Self {
45+
Self(Box::new(err))
46+
}
47+
}
48+
49+
impl Deref for IOError {
50+
type Target = Box<dyn std::error::Error + Send + Sync>;
51+
52+
fn deref(&self) -> &Self::Target {
53+
&self.0
54+
}
55+
}

src/extract/args/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use anyhow::Context as AnyhowCtx;
22
use serde::de::DeserializeOwned;
33
use std::ops::Deref;
44

5+
use crate::errors::ExtractionError;
56
use crate::system::{FromSystem, System};
6-
use crate::task::{Context, FromContext, InputError};
7+
use crate::task::{Context, FromContext};
78

89
mod de;
910
mod error;
@@ -12,7 +13,7 @@ mod error;
1213
pub struct Args<T>(pub T);
1314

1415
impl<T: DeserializeOwned + Send> FromContext for Args<T> {
15-
type Error = InputError;
16+
type Error = ExtractionError;
1617

1718
fn from_context(context: &Context) -> Result<Self, Self::Error> {
1819
let args = &context.args;
@@ -28,7 +29,7 @@ impl<T: DeserializeOwned + Send> FromContext for Args<T> {
2829
}
2930

3031
impl<T: DeserializeOwned + Send> FromSystem for Args<T> {
31-
type Error = InputError;
32+
type Error = ExtractionError;
3233

3334
fn from_system(_: &System, context: &Context) -> Result<Self, Self::Error> {
3435
Self::from_context(context)

src/extract/path.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use std::fmt::{Display, Formatter};
22

3+
use crate::errors::ExtractionError;
34
use crate::system::{FromSystem, System};
4-
use crate::task::{Context, FromContext, InputError};
5+
use crate::task::{Context, FromContext};
56

67
pub struct Path(pub String);
78

89
impl FromContext for Path {
9-
type Error = InputError;
10+
type Error = ExtractionError;
1011

1112
fn from_context(context: &Context) -> Result<Self, Self::Error> {
1213
let path = context.path.to_str();
@@ -22,7 +23,7 @@ impl Display for Path {
2223
}
2324

2425
impl FromSystem for Path {
25-
type Error = InputError;
26+
type Error = ExtractionError;
2627

2728
fn from_system(_: &System, context: &Context) -> Result<Self, Self::Error> {
2829
Self::from_context(context)

src/extract/res.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use anyhow::Context as AnyhowCxt;
22
use std::ops::Deref;
33
use std::sync::Arc;
44

5+
use crate::errors::ExtractionError;
56
use crate::system::{FromSystem, System};
6-
use crate::task::{Context, InputError};
7+
use crate::task::Context;
78

89
pub struct Res<R>(Arc<R>);
910

1011
impl<R: Send + Sync + 'static> FromSystem for Res<R> {
11-
type Error = InputError;
12+
type Error = ExtractionError;
1213

1314
fn from_system(system: &System, _: &Context) -> Result<Self, Self::Error> {
1415
let arc = system
@@ -19,7 +20,7 @@ impl<R: Send + Sync + 'static> FromSystem for Res<R> {
1920
std::any::type_name::<R>()
2021
)
2122
})
22-
.map_err(InputError::from)?;
23+
.map_err(ExtractionError::from)?;
2324
Ok(Res(arc))
2425
}
2526
}

src/extract/system.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use anyhow::Context as AnyhowCtx;
22
use serde::de::DeserializeOwned;
33

4+
use crate::errors::ExtractionError;
45
use crate::system::{FromSystem, System as SystemState};
5-
use crate::task::{Context, InputError};
6+
use crate::task::Context;
67

78
/// Extracts the root of the system state
89
#[derive(Debug, Clone)]
910
pub struct System<S>(pub S);
1011

1112
impl<S: DeserializeOwned> FromSystem for System<S> {
12-
type Error = InputError;
13+
type Error = ExtractionError;
1314

1415
fn from_system(system: &SystemState, _: &Context) -> Result<Self, Self::Error> {
1516
// This will fail if the value cannot be deserialized into the target type

src/extract/target.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use anyhow::Context as AnyhowCxt;
22
use serde::de::DeserializeOwned;
33
use std::ops::Deref;
44

5+
use crate::errors::ExtractionError;
56
use crate::system::{FromSystem, System};
6-
use crate::task::{Context, FromContext, InputError};
7+
use crate::task::{Context, FromContext};
78

89
pub struct Target<T>(pub T);
910

1011
impl<T: DeserializeOwned> FromContext for Target<T> {
11-
type Error = InputError;
12+
type Error = ExtractionError;
1213

1314
fn from_context(context: &Context) -> Result<Self, Self::Error> {
1415
let value = &context.target;
@@ -26,7 +27,7 @@ impl<T: DeserializeOwned> FromContext for Target<T> {
2627
}
2728

2829
impl<T: DeserializeOwned> FromSystem for Target<T> {
29-
type Error = InputError;
30+
type Error = ExtractionError;
3031

3132
fn from_system(_: &System, context: &Context) -> Result<Self, Self::Error> {
3233
Self::from_context(context)

src/extract/view.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ use serde::Serialize;
1010
use serde_json::Value;
1111
use std::ops::{Deref, DerefMut};
1212

13+
use crate::errors::ExtractionError;
1314
use crate::path::Path;
1415
use crate::system::{FromSystem, System};
15-
use crate::task::{Context, Effect, Error, InputError, IntoResult, UnexpectedError};
16+
use crate::task::{Context, Effect, Error, IntoResult};
1617

1718
/// Extracts a pointer to a sub-element of the global state indicated
1819
/// by the path.
@@ -75,7 +76,7 @@ impl<T> Pointer<T> {
7576
}
7677

7778
impl<T: DeserializeOwned> FromSystem for Pointer<T> {
78-
type Error = InputError;
79+
type Error = ExtractionError;
7980

8081
fn from_system(system: &System, context: &Context) -> Result<Self, Self::Error> {
8182
let json_ptr = context.path.as_ref();
@@ -180,9 +181,10 @@ impl<T: Serialize> IntoResult<Patch> for Pointer<T> {
180181
fn into_result(self) -> Result<Patch, Error> {
181182
let before = self.initial;
182183
let after = if let Some(state) = self.state {
183-
serde_json::to_value(state)
184-
.with_context(|| "Failed to serialize pointer value")
185-
.map_err(UnexpectedError::from)?
184+
// This should not happen unless there is a bug (hopefully).
185+
// if this happens during worker operation, it will be catched
186+
// as a panic in the task
187+
serde_json::to_value(state).expect("failed to serialize pointer value")
186188
} else {
187189
Value::Null
188190
};
@@ -219,7 +221,7 @@ impl<T, E> From<Pointer<T>> for Effect<Pointer<T>, E> {
219221
pub struct View<T>(Pointer<T>);
220222

221223
impl<T: DeserializeOwned> FromSystem for View<T> {
222-
type Error = InputError;
224+
type Error = ExtractionError;
223225

224226
fn from_system(system: &System, context: &Context) -> Result<Self, Self::Error> {
225227
let pointer = Pointer::<T>::from_system(system, context)?;

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod path;
22
mod planner;
33
mod system;
44

5+
pub mod errors;
56
pub mod extract;
67
pub mod task;
78
pub mod worker;

0 commit comments

Comments
 (0)