Skip to main content

gestalt/
error.rs

1#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
2#[error("{message}")]
3/// Error returned by typed provider handlers and runtime helpers.
4pub struct Error {
5    status: Option<u16>,
6    message: String,
7    expose_message: bool,
8}
9
10pub(crate) const HTTP_BAD_REQUEST: u16 = 400;
11pub(crate) const HTTP_UNAUTHORIZED: u16 = 401;
12pub(crate) const HTTP_FORBIDDEN: u16 = 403;
13pub(crate) const HTTP_NOT_FOUND: u16 = 404;
14pub(crate) const HTTP_CONFLICT: u16 = 409;
15pub(crate) const HTTP_PRECONDITION_FAILED: u16 = 412;
16pub(crate) const HTTP_INTERNAL_SERVER_ERROR: u16 = 500;
17pub(crate) const HTTP_NOT_IMPLEMENTED: u16 = 501;
18pub(crate) const HTTP_UNAVAILABLE: u16 = 503;
19pub(crate) const INTERNAL_ERROR_MESSAGE: &str = "internal error";
20
21/// Convenient result alias for Gestalt SDK operations.
22pub type Result<T> = std::result::Result<T, Error>;
23
24impl Error {
25    /// Creates an error without an explicit HTTP status code override.
26    pub fn new(message: impl Into<String>) -> Self {
27        Self {
28            status: None,
29            message: message.into(),
30            expose_message: true,
31        }
32    }
33
34    /// Creates an error with an explicit HTTP status code.
35    pub fn with_status(status: u16, message: impl Into<String>) -> Self {
36        Self {
37            status: Some(status),
38            message: message.into(),
39            expose_message: true,
40        }
41    }
42
43    /// Creates a `400 Bad Request` error.
44    pub fn bad_request(message: impl Into<String>) -> Self {
45        Self::with_status(HTTP_BAD_REQUEST, message)
46    }
47
48    /// Creates a `500 Internal Server Error`.
49    pub fn internal(message: impl Into<String>) -> Self {
50        Self::with_status(HTTP_INTERNAL_SERVER_ERROR, message)
51    }
52
53    /// Creates a `404 Not Found` error.
54    pub fn not_found(message: impl Into<String>) -> Self {
55        Self::with_status(HTTP_NOT_FOUND, message)
56    }
57
58    /// Creates a `409 Conflict` error for resources that already exist.
59    pub fn already_exists(message: impl Into<String>) -> Self {
60        Self::with_status(HTTP_CONFLICT, message)
61    }
62
63    /// Creates a `412 Precondition Failed` error.
64    pub fn failed_precondition(message: impl Into<String>) -> Self {
65        Self::with_status(HTTP_PRECONDITION_FAILED, message)
66    }
67
68    /// Creates a `503 Service Unavailable` error.
69    pub fn unavailable(message: impl Into<String>) -> Self {
70        Self::with_status(HTTP_UNAVAILABLE, message)
71    }
72
73    /// Creates a `401 Unauthorized` error.
74    pub fn unauthenticated(message: impl Into<String>) -> Self {
75        Self::with_status(HTTP_UNAUTHORIZED, message)
76    }
77
78    /// Creates a `403 Forbidden` error.
79    pub fn permission_denied(message: impl Into<String>) -> Self {
80        Self::with_status(HTTP_FORBIDDEN, message)
81    }
82
83    /// Creates a `501 Not Implemented` error.
84    pub fn unimplemented(message: impl Into<String>) -> Self {
85        Self::with_status(HTTP_NOT_IMPLEMENTED, message)
86    }
87
88    /// Returns the HTTP status code that should be used for this error, when
89    /// one was supplied.
90    pub fn status(&self) -> Option<u16> {
91        self.status
92    }
93
94    /// Returns the human-readable error message.
95    pub fn message(&self) -> &str {
96        &self.message
97    }
98
99    pub(crate) fn expose_message(&self) -> bool {
100        self.expose_message
101    }
102
103    pub(crate) fn hidden_internal(message: impl Into<String>) -> Self {
104        Self {
105            status: Some(HTTP_INTERNAL_SERVER_ERROR),
106            message: message.into(),
107            expose_message: false,
108        }
109    }
110}
111
112impl From<serde_json::Error> for Error {
113    fn from(value: serde_json::Error) -> Self {
114        Self::hidden_internal(value.to_string())
115    }
116}
117
118impl From<std::io::Error> for Error {
119    fn from(value: std::io::Error) -> Self {
120        Self::hidden_internal(value.to_string())
121    }
122}
123
124impl From<tonic::transport::Error> for Error {
125    fn from(value: tonic::transport::Error) -> Self {
126        Self::hidden_internal(value.to_string())
127    }
128}