Skip to main content

a3s_boot/
error.rs

1use thiserror::Error;
2
3/// Errors returned while building or serving a Boot application.
4#[derive(Debug, Error)]
5pub enum BootError {
6    #[error("module name cannot be empty")]
7    EmptyModuleName,
8    #[error("route path must start with '/': {0}")]
9    InvalidRoutePath(String),
10    #[error("host pattern is invalid: {0}")]
11    InvalidHostPattern(String),
12    #[error("route is already registered: {0}")]
13    DuplicateRoute(String),
14    #[error("route was not found: {0}")]
15    NotFound(String),
16    #[error("method is not allowed: {0}")]
17    MethodNotAllowed(String),
18    #[error("provider token is already registered: {0}")]
19    DuplicateProvider(String),
20    #[error("provider token is not registered: {0}")]
21    MissingProvider(String),
22    #[error("provider token has a different concrete type: {0}")]
23    ProviderTypeMismatch(String),
24    #[error("request was forbidden: {0}")]
25    Forbidden(String),
26    #[error("request was unauthorized: {0}")]
27    Unauthorized(String),
28    #[error("bad request: {0}")]
29    BadRequest(String),
30    #[error("payload is too large: {0}")]
31    PayloadTooLarge(String),
32    #[error("unsupported media type: {0}")]
33    UnsupportedMediaType(String),
34    #[error("not acceptable: {0}")]
35    NotAcceptable(String),
36    #[error("too many requests: {0}")]
37    TooManyRequests(String),
38    #[error("adapter error: {0}")]
39    Adapter(String),
40    #[error("internal error: {0}")]
41    Internal(String),
42    #[error("I/O error: {0}")]
43    Io(#[from] std::io::Error),
44}
45
46/// Stable category for a [`BootError`], useful for Nest-style catch filters.
47#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
48pub enum BootErrorKind {
49    EmptyModuleName,
50    InvalidRoutePath,
51    InvalidHostPattern,
52    DuplicateRoute,
53    NotFound,
54    MethodNotAllowed,
55    DuplicateProvider,
56    MissingProvider,
57    ProviderTypeMismatch,
58    Forbidden,
59    Unauthorized,
60    BadRequest,
61    PayloadTooLarge,
62    UnsupportedMediaType,
63    NotAcceptable,
64    TooManyRequests,
65    Adapter,
66    Internal,
67    Io,
68}
69
70impl BootError {
71    /// Stable category for this error.
72    pub fn kind(&self) -> BootErrorKind {
73        match self {
74            Self::EmptyModuleName => BootErrorKind::EmptyModuleName,
75            Self::InvalidRoutePath(_) => BootErrorKind::InvalidRoutePath,
76            Self::InvalidHostPattern(_) => BootErrorKind::InvalidHostPattern,
77            Self::DuplicateRoute(_) => BootErrorKind::DuplicateRoute,
78            Self::NotFound(_) => BootErrorKind::NotFound,
79            Self::MethodNotAllowed(_) => BootErrorKind::MethodNotAllowed,
80            Self::DuplicateProvider(_) => BootErrorKind::DuplicateProvider,
81            Self::MissingProvider(_) => BootErrorKind::MissingProvider,
82            Self::ProviderTypeMismatch(_) => BootErrorKind::ProviderTypeMismatch,
83            Self::Forbidden(_) => BootErrorKind::Forbidden,
84            Self::Unauthorized(_) => BootErrorKind::Unauthorized,
85            Self::BadRequest(_) => BootErrorKind::BadRequest,
86            Self::PayloadTooLarge(_) => BootErrorKind::PayloadTooLarge,
87            Self::UnsupportedMediaType(_) => BootErrorKind::UnsupportedMediaType,
88            Self::NotAcceptable(_) => BootErrorKind::NotAcceptable,
89            Self::TooManyRequests(_) => BootErrorKind::TooManyRequests,
90            Self::Adapter(_) => BootErrorKind::Adapter,
91            Self::Internal(_) => BootErrorKind::Internal,
92            Self::Io(_) => BootErrorKind::Io,
93        }
94    }
95
96    /// HTTP status code that adapters should use for this error.
97    pub fn http_status_code(&self) -> u16 {
98        match self {
99            Self::NotFound(_) => 404,
100            Self::MethodNotAllowed(_) => 405,
101            Self::Unauthorized(_) => 401,
102            Self::Forbidden(_) => 403,
103            Self::BadRequest(_) => 400,
104            Self::PayloadTooLarge(_) => 413,
105            Self::UnsupportedMediaType(_) => 415,
106            Self::NotAcceptable(_) => 406,
107            Self::TooManyRequests(_) => 429,
108            _ => 500,
109        }
110    }
111
112    /// Text response body that adapters should use for this error.
113    pub fn http_response_message(&self) -> String {
114        match self {
115            Self::NotFound(message)
116            | Self::MethodNotAllowed(message)
117            | Self::Unauthorized(message)
118            | Self::Forbidden(message)
119            | Self::BadRequest(message)
120            | Self::PayloadTooLarge(message)
121            | Self::UnsupportedMediaType(message)
122            | Self::NotAcceptable(message)
123            | Self::TooManyRequests(message) => message.clone(),
124            error => error.to_string(),
125        }
126    }
127
128    pub(crate) fn clone_for_filter(&self) -> Self {
129        match self {
130            Self::EmptyModuleName => Self::EmptyModuleName,
131            Self::InvalidRoutePath(message) => Self::InvalidRoutePath(message.clone()),
132            Self::InvalidHostPattern(message) => Self::InvalidHostPattern(message.clone()),
133            Self::DuplicateRoute(message) => Self::DuplicateRoute(message.clone()),
134            Self::NotFound(message) => Self::NotFound(message.clone()),
135            Self::MethodNotAllowed(message) => Self::MethodNotAllowed(message.clone()),
136            Self::DuplicateProvider(message) => Self::DuplicateProvider(message.clone()),
137            Self::MissingProvider(message) => Self::MissingProvider(message.clone()),
138            Self::ProviderTypeMismatch(message) => Self::ProviderTypeMismatch(message.clone()),
139            Self::Forbidden(message) => Self::Forbidden(message.clone()),
140            Self::Unauthorized(message) => Self::Unauthorized(message.clone()),
141            Self::BadRequest(message) => Self::BadRequest(message.clone()),
142            Self::PayloadTooLarge(message) => Self::PayloadTooLarge(message.clone()),
143            Self::UnsupportedMediaType(message) => Self::UnsupportedMediaType(message.clone()),
144            Self::NotAcceptable(message) => Self::NotAcceptable(message.clone()),
145            Self::TooManyRequests(message) => Self::TooManyRequests(message.clone()),
146            Self::Adapter(message) => Self::Adapter(message.clone()),
147            Self::Internal(message) => Self::Internal(message.clone()),
148            Self::Io(error) => Self::Io(std::io::Error::new(error.kind(), error.to_string())),
149        }
150    }
151}