Last active
August 2, 2025 08:08
-
-
Save luca992/ad305d1e39fb9cfeae91bf997607654f to your computer and use it in GitHub Desktop.
Rust Snafu Errors Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use snafu::Snafu; | |
| /// Error type for all API traits | |
| /// Note: Do not put implementation specific errors here (so no errors from reqwest, serde, etc.) | |
| /// These should be generic errors that can be used by any API implementation. | |
| /// The implementation specific errors should be defined locally for each API implementation then | |
| /// converted to this error type before being returned. | |
| /// | |
| /// Hint: use [From] to convert implementation specific errors to this error type. You can define | |
| /// an error snafu enum inside the same file as an api implementation and also implement the | |
| /// conversion to this error type in the same file using the [From] trait. | |
| #[derive(Debug, Snafu)] | |
| #[snafu(visibility(pub))] | |
| pub enum ApiError { | |
| #[snafu(display("Connection error: {}", details))] | |
| ConnectionError { details: String }, | |
| #[snafu(display("Invalid request: {}", details))] | |
| InvalidRequest { details: String }, | |
| #[snafu(display("{}", source))] | |
| InvalidData { source: InvalidDataError }, | |
| #[snafu(display("Resource not found: {}", details))] | |
| NotFound { details: String }, | |
| #[snafu(display("Resource already exists: {}", details))] | |
| AlreadyExists { details: String }, | |
| #[snafu(display("Unexpected response: {}", details))] | |
| UnexpectedResponse { details: String }, | |
| #[snafu(display("{}", message))] | |
| Unexpected { message: String }, | |
| #[snafu(display("Request failed: {}", details))] | |
| RequestFailed { details: String }, | |
| #[snafu(display("Too many requests: {}", details))] | |
| TooManyRequests { details: String }, | |
| #[snafu(display("Permission error: {}", source))] | |
| Permission { | |
| source: crate::error::PermissionError, | |
| }, | |
| #[snafu(display("Internal error: {}", source))] | |
| Internal { | |
| // Using a boxed trait object so that any error type can be wrapped. | |
| source: Box<dyn std::error::Error + Send + Sync>, | |
| }, | |
| } | |
| #[derive(Debug, Snafu)] | |
| #[snafu(visibility(pub))] | |
| pub enum RepositoryError { | |
| #[snafu(display("Entity not found: {details}"))] | |
| NotFound { details: String }, | |
| #[snafu(display("Already exists: {details}"))] | |
| AlreadyExists { details: String }, | |
| #[snafu(display("Invalid data: {source}"))] | |
| InvalidData { source: InvalidDataError }, | |
| #[snafu(display("Unexpected error: {message}"))] | |
| Unexpected { message: String }, | |
| } | |
| #[derive(Debug, Snafu)] | |
| #[snafu(visibility(pub))] | |
| pub enum InvalidDataError { | |
| #[snafu(display("Invalid input: {details}"))] | |
| InvalidArg { details: String }, | |
| #[snafu(display("Unexpected: {details}"))] | |
| UnexpectedDataState { details: String }, | |
| #[snafu(display("Unavailable: {details}"))] | |
| Unavailable { details: String }, | |
| #[snafu(display("Parse error: {details}"))] | |
| ParseError { details: String }, | |
| #[snafu(display("Serialization error: {details}"))] | |
| SerializationError { details: String }, | |
| } | |
| #[derive(Debug, Snafu)] | |
| #[snafu(visibility(pub))] | |
| pub enum PermissionError { | |
| #[snafu(display("Unauthorized: {details}"))] | |
| Unauthorized { details: String }, | |
| } | |
| // Helpers | |
| impl PermissionError { | |
| pub fn unauthorized<T: ToString>(details: T) -> Self { | |
| Self::Unauthorized { | |
| details: details.to_string(), | |
| } | |
| } | |
| } | |
| impl InvalidDataError { | |
| pub fn invalid_arg<T: ToString>(details: T) -> InvalidDataError { | |
| InvalidDataError::InvalidArg { | |
| details: details.to_string(), | |
| } | |
| } | |
| pub fn unexpected_data_state<T: ToString>(details: T) -> InvalidDataError { | |
| InvalidDataError::UnexpectedDataState { | |
| details: details.to_string(), | |
| } | |
| } | |
| pub fn unavailable<T: ToString>(details: T) -> InvalidDataError { | |
| InvalidDataError::Unavailable { | |
| details: details.to_string(), | |
| } | |
| } | |
| pub fn parse_error<T: ToString>(details: T) -> InvalidDataError { | |
| InvalidDataError::ParseError { | |
| details: details.to_string(), | |
| } | |
| } | |
| pub fn serialization_error<T: ToString>(details: T) -> InvalidDataError { | |
| InvalidDataError::SerializationError { | |
| details: details.to_string(), | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment