Skip to main content

espresso_api/
error.rs

1//! Typed API errors for proper HTTP/gRPC status code mapping
2
3use std::fmt;
4
5use thiserror::Error;
6
7/// Marker errors for availability endpoint failures. These are wrapped in `anyhow::Error` by the
8/// state implementation and downcasted in the Axum handlers to select the right HTTP status code.
9#[derive(Debug, Error)]
10pub enum AvailabilityError {
11    #[error("{0}")]
12    NotFound(String),
13    #[error("{0}")]
14    RangeExceeded(String),
15    #[error("{0}")]
16    BadRequest(String),
17}
18
19/// API error types that can be downcast at the HTTP/gRPC boundary
20#[derive(Debug)]
21pub enum ApiError {
22    /// Client provided invalid input (maps to 400 Bad Request / INVALID_ARGUMENT)
23    BadRequest(anyhow::Error),
24    /// Requested resource does not exist (maps to 404 Not Found)
25    NotFound(anyhow::Error),
26    /// Handler failed for any reason (maps to 500 Internal Server Error / INTERNAL)
27    Internal(anyhow::Error),
28}
29
30impl fmt::Display for ApiError {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        match self {
33            ApiError::BadRequest(err) | ApiError::NotFound(err) | ApiError::Internal(err) => {
34                write!(f, "{}", err)
35            },
36        }
37    }
38}
39
40impl std::error::Error for ApiError {
41    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
42        match self {
43            ApiError::BadRequest(err) | ApiError::NotFound(err) | ApiError::Internal(err) => {
44                err.source()
45            },
46        }
47    }
48}