use std::time::Duration;
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SdkError {
#[error("{0}")]
InvalidConfig(String),
#[error("{0}")]
UnsupportedExecutor(String),
#[error("serialization failed: {0}")]
Serialization(String),
#[error("job timed out after {0:?}")]
Timeout(Duration),
#[error("job was cancelled")]
Cancelled,
#[error("job failed: {0}")]
JobFailed(String),
#[error("unexpected coordinator response: {0}")]
UnexpectedResponse(String),
#[error("recurser: {0}")]
Recurser(String),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Backend(BoxError),
}
impl SdkError {
pub(crate) fn backend(err: impl Into<BoxError>) -> Self {
Self::Backend(err.into())
}
}
impl From<tokio::task::JoinError> for SdkError {
fn from(err: tokio::task::JoinError) -> Self {
Self::Backend(Box::new(err))
}
}
pub type Result<T, E = SdkError> = core::result::Result<T, E>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn backend_wraps_into_backend_variant_and_is_transparent() {
let io = std::io::Error::other("disk on fire");
let err = SdkError::backend(io);
assert!(matches!(err, SdkError::Backend(_)));
assert_eq!(err.to_string(), "disk on fire");
}
#[test]
fn io_error_routes_to_io_variant_not_backend() {
let err: SdkError = std::io::Error::new(std::io::ErrorKind::NotFound, "nope").into();
assert!(matches!(err, SdkError::Io(_)), "io::Error should map to SdkError::Io");
assert_eq!(err.to_string(), "nope");
}
#[tokio::test]
async fn join_error_maps_to_backend() {
let handle = tokio::spawn(async {
std::future::pending::<()>().await;
});
handle.abort();
let join_err = handle.await.expect_err("aborted task yields a JoinError");
assert!(join_err.is_cancelled());
let err: SdkError = join_err.into();
assert!(matches!(err, SdkError::Backend(_)));
}
#[test]
fn structured_variants_format_as_documented() {
assert_eq!(SdkError::Cancelled.to_string(), "job was cancelled");
assert_eq!(
SdkError::Timeout(std::time::Duration::from_secs(3)).to_string(),
"job timed out after 3s"
);
assert_eq!(SdkError::JobFailed("boom".into()).to_string(), "job failed: boom");
}
}