1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use serde::{Deserialize, Serialize};
/// Machine-readable error codes that remain stable across releases.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Code {
/// Internal server error (500).
Internal,
/// Bad request error (400).
BadRequest,
/// Resource not found (404).
NotFound,
/// HTTP method not allowed (405).
MethodNotAllowed,
/// Resource permanently deleted (410).
Gone,
/// Resource conflict, often due to concurrent modification (409).
Conflict,
/// Request payload exceeds size limit (413).
PayloadTooLarge,
/// Request timed out before completion (408).
RequestTimeout,
/// Too many requests, client should retry later (429).
RateLimited,
/// Service temporarily unavailable (503).
Unavailable,
/// Input validation failed (400).
ValidationFailed,
/// Authentication required or credentials invalid (401).
Unauthorized,
/// Authenticated but not permitted to access resource (403).
Forbidden,
/// Request is well-formed but semantically invalid (422).
UnprocessableEntity,
/// Gateway or processing timeout (504).
Timeout,
/// Request was canceled by client (499).
Canceled,
/// Downstream service returned an error (502).
DownstreamError,
/// Downstream service timed out (504).
DownstreamTimeout,
}
impl Code {
/// Returns the default HTTP status code for this error code.
pub fn default_status(&self) -> u16 {
match self {
Code::Internal => 500,
Code::BadRequest => 400,
Code::NotFound => 404,
Code::MethodNotAllowed => 405,
Code::Gone => 410,
Code::Conflict => 409,
Code::PayloadTooLarge => 413,
Code::RequestTimeout => 408,
Code::RateLimited => 429,
Code::Unavailable => 503,
Code::ValidationFailed => 400,
Code::Unauthorized => 401,
Code::Forbidden => 403,
Code::UnprocessableEntity => 422,
Code::Timeout => 504,
Code::Canceled => 499,
Code::DownstreamError => 502,
Code::DownstreamTimeout => 504,
}
}
/// Returns whether this error is retryable by default.
pub fn is_retryable_default(&self) -> bool {
matches!(
self,
Code::Timeout
| Code::DownstreamTimeout
| Code::Unavailable
| Code::RateLimited
| Code::RequestTimeout
)
}
/// Returns a default human-readable message for this code.
pub fn default_message(&self) -> &'static str {
match self {
Code::Internal => "Internal error",
Code::BadRequest => "Bad request",
Code::ValidationFailed => "Invalid input",
Code::Unauthorized => "Unauthorized",
Code::Forbidden => "Forbidden",
Code::NotFound => "Not found",
Code::Gone => "Resource no longer exists",
Code::Conflict => "Conflict",
Code::PayloadTooLarge => "Payload too large",
Code::UnprocessableEntity => "Unprocessable entity",
Code::RateLimited => "Rate limited",
Code::RequestTimeout | Code::Timeout | Code::DownstreamTimeout => "Request timed out",
Code::Unavailable => "Service unavailable",
Code::Canceled => "Request canceled",
Code::DownstreamError => "Downstream service error",
Code::MethodNotAllowed => "Method not allowed",
}
}
}