error_envelope/
codes.rs

1use serde::{Deserialize, Serialize};
2
3/// Machine-readable error codes that remain stable across releases.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
6pub enum Code {
7    // Generic errors
8    Internal,
9    BadRequest,
10    NotFound,
11    MethodNotAllowed,
12    Gone,
13    Conflict,
14    PayloadTooLarge,
15    RequestTimeout,
16    RateLimited,
17    Unavailable,
18
19    // Validation / auth
20    ValidationFailed,
21    Unauthorized,
22    Forbidden,
23    UnprocessableEntity,
24
25    // Timeouts / cancellations
26    Timeout,
27    Canceled,
28
29    // Downstream
30    DownstreamError,
31    DownstreamTimeout,
32}
33
34impl Code {
35    /// Returns the default HTTP status code for this error code.
36    pub fn default_status(&self) -> u16 {
37        match self {
38            Code::Internal => 500,
39            Code::BadRequest => 400,
40            Code::NotFound => 404,
41            Code::MethodNotAllowed => 405,
42            Code::Gone => 410,
43            Code::Conflict => 409,
44            Code::PayloadTooLarge => 413,
45            Code::RequestTimeout => 408,
46            Code::RateLimited => 429,
47            Code::Unavailable => 503,
48            Code::ValidationFailed => 400,
49            Code::Unauthorized => 401,
50            Code::Forbidden => 403,
51            Code::UnprocessableEntity => 422,
52            Code::Timeout => 504,
53            Code::Canceled => 499,
54            Code::DownstreamError => 502,
55            Code::DownstreamTimeout => 504,
56        }
57    }
58
59    /// Returns whether this error is retryable by default.
60    pub fn is_retryable_default(&self) -> bool {
61        matches!(
62            self,
63            Code::Timeout
64                | Code::DownstreamTimeout
65                | Code::Unavailable
66                | Code::RateLimited
67                | Code::RequestTimeout
68        )
69    }
70
71    /// Returns a default human-readable message for this code.
72    pub fn default_message(&self) -> &'static str {
73        match self {
74            Code::Internal => "Internal error",
75            Code::BadRequest => "Bad request",
76            Code::ValidationFailed => "Invalid input",
77            Code::Unauthorized => "Unauthorized",
78            Code::Forbidden => "Forbidden",
79            Code::NotFound => "Not found",
80            Code::Gone => "Resource no longer exists",
81            Code::Conflict => "Conflict",
82            Code::PayloadTooLarge => "Payload too large",
83            Code::UnprocessableEntity => "Unprocessable entity",
84            Code::RateLimited => "Rate limited",
85            Code::RequestTimeout | Code::Timeout | Code::DownstreamTimeout => "Request timed out",
86            Code::Unavailable => "Service unavailable",
87            Code::Canceled => "Request canceled",
88            Code::DownstreamError => "Downstream service error",
89            Code::MethodNotAllowed => "Method not allowed",
90        }
91    }
92}