typesafe_systemone/
error.rs1use std::time::Duration;
2
3pub type Result<T> = std::result::Result<T, Error>;
5
6#[derive(Debug, thiserror::Error)]
10#[non_exhaustive]
11pub enum Error {
12 #[error("configuration error: {0}")]
14 Config(String),
15
16 #[error("invalid request: {0}")]
18 InvalidRequest(String),
19
20 #[error("no {expected} answer for question `{id}`")]
22 MissingAnswer {
23 id: String,
25 expected: &'static str,
27 },
28
29 #[error("bad request: {message}")]
31 BadRequest {
32 message: String,
34 },
35
36 #[error("authentication failed: {message}")]
38 Authentication {
39 message: String,
41 },
42
43 #[error("permission denied: {message}")]
45 PermissionDenied {
46 message: String,
48 },
49
50 #[error("not found: {message}")]
52 NotFound {
53 message: String,
55 },
56
57 #[error("unprocessable entity: {message}")]
59 UnprocessableEntity {
60 message: String,
62 },
63
64 #[error("rate limited: {message}")]
66 RateLimit {
67 retry_after: Option<Duration>,
69 message: String,
71 },
72
73 #[error("overloaded: {message}")]
75 Overloaded {
76 retry_after: Option<Duration>,
78 message: String,
80 },
81
82 #[error("server error {status}: {message}")]
84 Server {
85 status: u16,
87 message: String,
89 },
90
91 #[error("unexpected status {status}: {message}")]
93 UnexpectedStatus {
94 status: u16,
96 message: String,
98 },
99
100 #[error("connection error: {0}")]
102 Connection(#[source] reqwest::Error),
103
104 #[error("request timed out")]
106 Timeout(#[source] reqwest::Error),
107
108 #[error("response validation failed: {0}")]
110 ResponseValidation(#[source] serde_json::Error),
111
112 #[error("request serialization failed: {0}")]
114 RequestSerialization(#[source] serde_json::Error),
115}
116
117impl Error {
118 #[must_use]
120 pub const fn status(&self) -> Option<u16> {
121 match self {
122 Self::BadRequest { .. } => Some(400),
123 Self::Authentication { .. } => Some(401),
124 Self::PermissionDenied { .. } => Some(403),
125 Self::NotFound { .. } => Some(404),
126 Self::UnprocessableEntity { .. } => Some(422),
127 Self::RateLimit { .. } => Some(429),
128 Self::Overloaded { .. } => Some(529),
129 Self::Server { status, .. } | Self::UnexpectedStatus { status, .. } => Some(*status),
130 _ => None,
131 }
132 }
133
134 pub(crate) fn from_status(status: u16, retry_after: Option<Duration>, message: String) -> Self {
135 match status {
136 400 => Self::BadRequest { message },
137 401 => Self::Authentication { message },
138 403 => Self::PermissionDenied { message },
139 404 => Self::NotFound { message },
140 422 => Self::UnprocessableEntity { message },
141 429 => Self::RateLimit { retry_after, message },
142 529 => Self::Overloaded { retry_after, message },
143 500..=599 => Self::Server { status, message },
144 _ => Self::UnexpectedStatus { status, message },
145 }
146 }
147
148 pub(crate) fn from_transport(err: reqwest::Error) -> Self {
149 if err.is_timeout() {
150 Self::Timeout(err)
151 } else {
152 Self::Connection(err)
153 }
154 }
155}