Skip to main content

unitycatalog_common/
error.rs

1/// A convenience type for declaring Results in the Delta Sharing libraries.
2pub type Result<T, E = Error> = std::result::Result<T, E>;
3
4#[derive(Debug, thiserror::Error)]
5pub enum Error {
6    #[error("Entity not found.")]
7    NotFound,
8
9    #[error("Already exists")]
10    AlreadyExists,
11
12    #[error("Invalid table location: {0}")]
13    InvalidTableLocation(String),
14
15    #[error("Invalid Argument: {0}")]
16    InvalidArgument(String),
17
18    #[error("Invalid identifier: {0}")]
19    InvalidIdentifier(#[from] uuid::Error),
20
21    #[error("Generic error: {0}")]
22    Generic(String),
23
24    #[error(transparent)]
25    SerDe(#[from] serde_json::Error),
26
27    #[error("invalid url: {0}")]
28    InvalidUrl(#[from] url::ParseError),
29
30    #[error(transparent)]
31    ResourceStore(#[from] olai_store::Error),
32}
33
34impl Error {
35    pub fn generic(msg: impl Into<String>) -> Self {
36        Self::Generic(msg.into())
37    }
38
39    pub fn invalid_argument(msg: impl Into<String>) -> Self {
40        Self::InvalidArgument(msg.into())
41    }
42
43    /// Returns a machine-readable error code matching the UC API spec.
44    pub fn error_code(&self) -> &str {
45        match self {
46            Error::NotFound => "RESOURCE_NOT_FOUND",
47            Error::AlreadyExists => "RESOURCE_ALREADY_EXISTS",
48            Error::InvalidArgument(_) => "INVALID_PARAMETER_VALUE",
49            Error::InvalidIdentifier(_) => "INVALID_PARAMETER_VALUE",
50            Error::InvalidTableLocation(_) => "INVALID_PARAMETER_VALUE",
51            Error::InvalidUrl(_) => "INVALID_PARAMETER_VALUE",
52            Error::SerDe(_) => "INTERNAL_ERROR",
53            Error::Generic(_) => "INTERNAL_ERROR",
54            Error::ResourceStore(e) => match e {
55                olai_store::Error::NotFound => "RESOURCE_NOT_FOUND",
56                olai_store::Error::AlreadyExists => "ALREADY_EXISTS",
57                olai_store::Error::InvalidArgument(_) => "INVALID_PARAMETER_VALUE",
58                olai_store::Error::InvalidIdentifier(_) => "INVALID_PARAMETER_VALUE",
59                _ => "INTERNAL_ERROR",
60            },
61        }
62    }
63}
64
65#[cfg(feature = "axum")]
66impl Error {
67    /// Maps this error to an HTTP status and a static client-facing message.
68    ///
69    /// Shared by downstream `IntoResponse` impls (server, sharing-client) so the
70    /// status/message table for common variants lives in one place. Callers
71    /// remain responsible for composing the `error_code` and wrapping the result
72    /// in their own `ErrorResponse` body.
73    pub fn response_parts(&self) -> (http::StatusCode, &'static str) {
74        use http::StatusCode;
75
76        const INTERNAL: &str = "The request is not handled correctly due to a server error.";
77        const INVALID: &str = "Invalid argument provided in the request.";
78        const NOT_FOUND: &str = "The requested resource does not exist.";
79        const ALREADY_EXISTS: &str = "The resource already exists.";
80
81        match self {
82            Error::NotFound => (StatusCode::NOT_FOUND, NOT_FOUND),
83            Error::AlreadyExists => (StatusCode::CONFLICT, ALREADY_EXISTS),
84            Error::InvalidArgument(msg) => {
85                tracing::error!("Invalid argument: {msg}");
86                (StatusCode::BAD_REQUEST, INVALID)
87            }
88            Error::InvalidIdentifier(e) => {
89                tracing::error!("Invalid identifier: {e}");
90                (StatusCode::BAD_REQUEST, INVALID)
91            }
92            Error::InvalidTableLocation(loc) => {
93                tracing::error!("Invalid table location: {loc}");
94                (StatusCode::BAD_REQUEST, INVALID)
95            }
96            Error::InvalidUrl(e) => {
97                tracing::error!("Invalid URL: {e}");
98                (StatusCode::BAD_REQUEST, INVALID)
99            }
100            Error::SerDe(e) => {
101                tracing::error!("Serialization error: {e}");
102                (StatusCode::INTERNAL_SERVER_ERROR, INTERNAL)
103            }
104            Error::Generic(msg) => {
105                tracing::error!("Generic common error: {msg}");
106                (StatusCode::INTERNAL_SERVER_ERROR, INTERNAL)
107            }
108            Error::ResourceStore(e) => match e {
109                olai_store::Error::NotFound => (StatusCode::NOT_FOUND, NOT_FOUND),
110                olai_store::Error::AlreadyExists => (StatusCode::CONFLICT, ALREADY_EXISTS),
111                olai_store::Error::InvalidArgument(msg) => {
112                    tracing::error!("Invalid argument: {msg}");
113                    (StatusCode::BAD_REQUEST, INVALID)
114                }
115                olai_store::Error::InvalidIdentifier(e) => {
116                    tracing::error!("Invalid identifier: {e}");
117                    (StatusCode::BAD_REQUEST, INVALID)
118                }
119                _ => {
120                    tracing::error!("Resource store error: {e}");
121                    (StatusCode::INTERNAL_SERVER_ERROR, INTERNAL)
122                }
123            },
124        }
125    }
126}