error_envelope/
helpers.rs1use crate::{Code, Error};
2
3impl Error {
5 pub fn internal(message: impl Into<String>) -> Self {
9 Self::new(Code::Internal, 500, message).with_retryable(false)
10 }
11
12 pub fn bad_request(message: impl Into<String>) -> Self {
14 Self::new(Code::BadRequest, 400, message).with_retryable(false)
15 }
16
17 pub fn validation(message: impl Into<String>) -> Self {
19 Self::new(Code::ValidationFailed, 400, message).with_retryable(false)
20 }
21
22 pub fn unauthorized(message: impl Into<String>) -> Self {
24 Self::new(Code::Unauthorized, 401, message).with_retryable(false)
25 }
26
27 pub fn forbidden(message: impl Into<String>) -> Self {
29 Self::new(Code::Forbidden, 403, message).with_retryable(false)
30 }
31
32 pub fn not_found(message: impl Into<String>) -> Self {
34 Self::new(Code::NotFound, 404, message).with_retryable(false)
35 }
36
37 pub fn method_not_allowed(message: impl Into<String>) -> Self {
39 Self::new(Code::MethodNotAllowed, 405, message).with_retryable(false)
40 }
41
42 pub fn request_timeout(message: impl Into<String>) -> Self {
44 Self::new(Code::RequestTimeout, 408, message).with_retryable(true)
45 }
46
47 pub fn conflict(message: impl Into<String>) -> Self {
49 Self::new(Code::Conflict, 409, message).with_retryable(false)
50 }
51
52 pub fn gone(message: impl Into<String>) -> Self {
54 Self::new(Code::Gone, 410, message).with_retryable(false)
55 }
56
57 pub fn payload_too_large(message: impl Into<String>) -> Self {
59 Self::new(Code::PayloadTooLarge, 413, message).with_retryable(false)
60 }
61
62 pub fn unprocessable_entity(message: impl Into<String>) -> Self {
64 Self::new(Code::UnprocessableEntity, 422, message).with_retryable(false)
65 }
66
67 pub fn rate_limited(message: impl Into<String>) -> Self {
69 Self::new(Code::RateLimited, 429, message).with_retryable(true)
70 }
71
72 pub fn timeout(message: impl Into<String>) -> Self {
74 Self::new(Code::Timeout, 504, message).with_retryable(true)
75 }
76
77 pub fn unavailable(message: impl Into<String>) -> Self {
79 Self::new(Code::Unavailable, 503, message).with_retryable(true)
80 }
81
82 pub fn downstream(service: impl Into<String>, cause: impl std::error::Error) -> Self {
84 let service = service.into();
85 let mut err = Self::wrap(Code::DownstreamError, 502, "", cause);
86 if !service.is_empty() {
87 err = err.with_details(serde_json::json!({"service": service}));
88 }
89 err.with_retryable(true)
90 }
91
92 pub fn downstream_timeout(service: impl Into<String>, cause: impl std::error::Error) -> Self {
94 let service = service.into();
95 let mut err = Self::wrap(Code::DownstreamTimeout, 504, "", cause);
96 if !service.is_empty() {
97 err = err.with_details(serde_json::json!({"service": service}));
98 }
99 err.with_retryable(true)
100 }
101}
102
103pub fn internalf(message: impl Into<String>) -> Error {
107 Error::internal(message)
108}
109
110pub fn bad_requestf(message: impl Into<String>) -> Error {
112 Error::bad_request(message)
113}
114
115pub fn not_foundf(message: impl Into<String>) -> Error {
117 Error::not_found(message)
118}
119
120pub fn unauthorizedf(message: impl Into<String>) -> Error {
122 Error::unauthorized(message)
123}
124
125pub fn forbiddenf(message: impl Into<String>) -> Error {
127 Error::forbidden(message)
128}
129
130pub fn conflictf(message: impl Into<String>) -> Error {
132 Error::conflict(message)
133}
134
135pub fn timeoutf(message: impl Into<String>) -> Error {
137 Error::timeout(message)
138}
139
140pub fn unavailablef(message: impl Into<String>) -> Error {
142 Error::unavailable(message)
143}
144
145use serde_json::json;
148use std::collections::HashMap;
149
150pub type FieldErrors = HashMap<String, String>;
152
153pub fn validation(fields: FieldErrors) -> Error {
155 Error::new(Code::ValidationFailed, 400, "")
156 .with_details(json!({"fields": fields}))
157 .with_retryable(false)
158}
159
160pub fn from(err: impl std::error::Error + 'static) -> Error {
164 let err_str = err.to_string().to_lowercase();
165
166 if err_str.contains("timeout") || err_str.contains("timed out") {
168 return Error::timeout("");
169 }
170
171 if err_str.contains("cancel") {
173 return Error::new(Code::Canceled, 499, "").with_retryable(false);
174 }
175
176 Error::wrap(Code::Internal, 500, "", err).with_retryable(false)
178}
179
180pub fn is(err: &Error, code: Code) -> bool {
182 err.code == code
183}