1use std::fmt;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[repr(u32)]
13pub enum ErrorCode {
14 NotFound = 1,
15 AlreadyExists = 2,
16 InvalidArgument = 3,
17 PermissionDenied = 4,
18 FailedPrecondition = 5,
19 ResourceExhausted = 6,
20 Unavailable = 7,
21 InternalError = 8,
22 NotSupported = 9,
23 Unknown = 10,
24}
25
26impl From<u32> for ErrorCode {
27 fn from(value: u32) -> Self {
28 match value {
29 1 => Self::NotFound,
30 2 => Self::AlreadyExists,
31 3 => Self::InvalidArgument,
32 4 => Self::PermissionDenied,
33 5 => Self::FailedPrecondition,
34 6 => Self::ResourceExhausted,
35 7 => Self::Unavailable,
36 8 => Self::InternalError,
37 9 => Self::NotSupported,
38 _ => Self::Unknown,
39 }
40 }
41}
42
43impl From<ErrorCode> for u32 {
44 fn from(value: ErrorCode) -> Self {
45 value as u32
46 }
47}
48
49impl fmt::Display for ErrorCode {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 let name = match self {
52 Self::NotFound => "NotFound",
53 Self::AlreadyExists => "AlreadyExists",
54 Self::InvalidArgument => "InvalidArgument",
55 Self::PermissionDenied => "PermissionDenied",
56 Self::FailedPrecondition => "FailedPrecondition",
57 Self::ResourceExhausted => "ResourceExhausted",
58 Self::Unavailable => "Unavailable",
59 Self::InternalError => "InternalError",
60 Self::NotSupported => "NotSupported",
61 Self::Unknown => "Unknown",
62 };
63 f.write_str(name)
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct Error {
70 pub code: ErrorCode,
72 pub message: String,
74}
75
76impl Error {
77 pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
79 Self {
80 code,
81 message: message.into(),
82 }
83 }
84
85 pub fn not_found(message: impl Into<String>) -> Self {
86 Self::new(ErrorCode::NotFound, message)
87 }
88
89 pub fn already_exists(message: impl Into<String>) -> Self {
90 Self::new(ErrorCode::AlreadyExists, message)
91 }
92
93 pub fn invalid_argument(message: impl Into<String>) -> Self {
94 Self::new(ErrorCode::InvalidArgument, message)
95 }
96
97 pub fn permission_denied(message: impl Into<String>) -> Self {
98 Self::new(ErrorCode::PermissionDenied, message)
99 }
100
101 pub fn failed_precondition(message: impl Into<String>) -> Self {
102 Self::new(ErrorCode::FailedPrecondition, message)
103 }
104
105 pub fn internal(message: impl Into<String>) -> Self {
106 Self::new(ErrorCode::InternalError, message)
107 }
108
109 pub fn resource_exhausted(message: impl Into<String>) -> Self {
110 Self::new(ErrorCode::ResourceExhausted, message)
111 }
112
113 pub fn not_supported(message: impl Into<String>) -> Self {
114 Self::new(ErrorCode::NotSupported, message)
115 }
116
117 pub fn is_not_found(&self) -> bool {
118 self.code == ErrorCode::NotFound
119 }
120
121 pub fn is_already_exists(&self) -> bool {
122 self.code == ErrorCode::AlreadyExists
123 }
124
125 pub fn is_invalid_argument(&self) -> bool {
126 self.code == ErrorCode::InvalidArgument
127 }
128}
129
130impl fmt::Display for Error {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(f, "a3s-vec error {}: {}", self.code, self.message)
133 }
134}
135
136impl std::error::Error for Error {}
137
138pub type Result<T> = std::result::Result<T, Error>;
140
141impl From<std::io::Error> for Error {
142 fn from(value: std::io::Error) -> Self {
143 let code = match value.kind() {
144 std::io::ErrorKind::NotFound => ErrorCode::NotFound,
145 std::io::ErrorKind::PermissionDenied => ErrorCode::PermissionDenied,
146 std::io::ErrorKind::AlreadyExists => ErrorCode::AlreadyExists,
147 _ => ErrorCode::InternalError,
148 };
149 Self::new(code, value.to_string())
150 }
151}
152
153impl From<serde_json::Error> for Error {
154 fn from(value: serde_json::Error) -> Self {
155 Self::new(ErrorCode::InternalError, format!("JSON error: {value}"))
156 }
157}
158
159impl From<zvec_core::error::ZvecError> for Error {
160 fn from(value: zvec_core::error::ZvecError) -> Self {
161 use zvec_core::error::ZvecError;
162 let (code, message) = match value {
163 ZvecError::NotFound(message) => (ErrorCode::NotFound, message),
164 ZvecError::AlreadyExists(message) => (ErrorCode::AlreadyExists, message),
165 ZvecError::InvalidArgument(message) => (ErrorCode::InvalidArgument, message),
166 ZvecError::PermissionDenied(message) => (ErrorCode::PermissionDenied, message),
167 ZvecError::FailedPrecondition(message) => (ErrorCode::FailedPrecondition, message),
168 ZvecError::ResourceExhausted(message) => (ErrorCode::ResourceExhausted, message),
169 ZvecError::Unavailable(message) => (ErrorCode::Unavailable, message),
170 ZvecError::Internal(message) => (ErrorCode::InternalError, message),
171 ZvecError::NotSupported(message) => (ErrorCode::NotSupported, message),
172 ZvecError::Unknown(message) => (ErrorCode::Unknown, message),
173 };
174 Self::new(code, message)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn status_codes_are_stable() {
184 assert_eq!(u32::from(ErrorCode::InvalidArgument), 3);
185 assert_eq!(ErrorCode::from(99), ErrorCode::Unknown);
186 }
187
188 #[test]
189 fn helpers_preserve_context() {
190 let error = Error::not_found("document x");
191 assert!(error.is_not_found());
192 assert!(error.to_string().contains("document x"));
193 }
194}