1use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum ErrorKind {
15 InvalidRequest,
17 Unauthorized,
19 ContentTooLarge,
23 PermissionDenied,
26 NotSupported,
29 NotFound,
31 MethodNotAllowed,
34 Gone,
37 AlreadyExists,
39 Conflict,
43 Unavailable,
45 OutcomeUnknown,
49 DataCorruption,
51 Internal,
53}
54
55macro_rules! error_codes {
62 (@count) => { 0 };
63 (@count $head:ident $($tail:ident)*) => { 1 + error_codes!(@count $($tail)*) };
64 ($($variant:ident => $wire:literal),+ $(,)?) => {
65 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
72 #[non_exhaustive]
73 pub enum ErrorCode {
74 $(
75 #[doc = concat!("Carries the stable wire code `", $wire, "`.")]
76 $variant,
77 )+
78 }
79
80 impl ErrorCode {
81 pub const ALL: [ErrorCode; error_codes!(@count $($variant)+)] =
83 [$(ErrorCode::$variant,)+];
84
85 pub fn as_str(self) -> &'static str {
87 match self {
88 $(ErrorCode::$variant => $wire,)+
89 }
90 }
91
92 pub fn parse(value: &str) -> Option<ErrorCode> {
95 match value {
96 $($wire => Some(ErrorCode::$variant),)+
97 _ => None,
98 }
99 }
100 }
101
102 impl serde::Serialize for ErrorCode {
103 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
104 serializer.serialize_str(self.as_str())
105 }
106 }
107
108 impl<'de> serde::Deserialize<'de> for ErrorCode {
109 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
114 let value = String::deserialize(deserializer)?;
115 ErrorCode::parse(&value).ok_or_else(|| {
116 serde::de::Error::custom(format_args!("unknown error code `{value}`"))
117 })
118 }
119 }
120 };
121}
122
123error_codes! {
124 InvalidRequest => "invalid_request",
125 Unauthorized => "unauthorized",
126 PermissionDenied => "permission_denied",
127 ContentTooLarge => "content_too_large",
128 NotSupported => "not_supported",
129 RouteNotFound => "route_not_found",
130 MethodNotAllowed => "method_not_allowed",
131 NamespaceNotFound => "namespace_not_found",
132 NamespaceDeleted => "namespace_deleted",
133 NamespaceExists => "namespace_exists",
134 ContentNotPrepared => "content_not_prepared",
135 PathNotFound => "path_not_found",
136 RevisionNotFound => "revision_not_found",
137 PathConflict => "path_conflict",
138 DirectoryNotEmpty => "directory_not_empty",
139 StaleHead => "stale_head",
140 StaleRevision => "stale_revision",
141 NotDeleted => "not_deleted",
142 WriterFenced => "writer_fenced",
143 WouldCycle => "would_cycle",
144 CommitIdReuseConflict => "commit_id_reuse_conflict",
145 CommitOutcomeUnknown => "commit_outcome_unknown",
146 CommitQueueFull => "commit_queue_full",
147 ServerBusy => "server_busy",
148 ShuttingDown => "shutting_down",
149 CheckpointUnavailable => "checkpoint_unavailable",
150 MaintenanceRequired => "maintenance_required",
151 UploadNotFound => "upload_not_found",
152 UploadAlreadyCompleted => "upload_already_completed",
153 UploadContentConflict => "upload_content_conflict",
154 RebootstrapRequired => "rebootstrap_required",
155 QueryUnindexable => "query_unindexable",
156 IndexLagging => "index_lagging",
157 IndexCorrupt => "index_corrupt",
158 NamespaceCorrupt => "namespace_corrupt",
159 ServerError => "server_error",
160}
161
162impl ErrorCode {
163 pub fn kind(self) -> ErrorKind {
168 match self {
169 ErrorCode::InvalidRequest | ErrorCode::QueryUnindexable => ErrorKind::InvalidRequest,
173 ErrorCode::Unauthorized => ErrorKind::Unauthorized,
174 ErrorCode::PermissionDenied => ErrorKind::PermissionDenied,
178 ErrorCode::ContentTooLarge => ErrorKind::ContentTooLarge,
179 ErrorCode::NotSupported => ErrorKind::NotSupported,
180 ErrorCode::NamespaceNotFound
181 | ErrorCode::PathNotFound
182 | ErrorCode::RevisionNotFound
183 | ErrorCode::UploadNotFound
184 | ErrorCode::RouteNotFound => ErrorKind::NotFound,
185 ErrorCode::MethodNotAllowed => ErrorKind::MethodNotAllowed,
186 ErrorCode::NamespaceDeleted => ErrorKind::Gone,
187 ErrorCode::NamespaceExists => ErrorKind::AlreadyExists,
188 ErrorCode::CommitQueueFull
191 | ErrorCode::ServerBusy
192 | ErrorCode::ShuttingDown
193 | ErrorCode::CheckpointUnavailable
194 | ErrorCode::IndexLagging
195 | ErrorCode::MaintenanceRequired => ErrorKind::Unavailable,
196 ErrorCode::CommitOutcomeUnknown => ErrorKind::OutcomeUnknown,
197 ErrorCode::IndexCorrupt | ErrorCode::NamespaceCorrupt => ErrorKind::DataCorruption,
198 ErrorCode::ServerError => ErrorKind::Internal,
199 ErrorCode::ContentNotPrepared
204 | ErrorCode::PathConflict
205 | ErrorCode::DirectoryNotEmpty
206 | ErrorCode::StaleHead
207 | ErrorCode::StaleRevision
208 | ErrorCode::NotDeleted
211 | ErrorCode::WriterFenced
212 | ErrorCode::WouldCycle
213 | ErrorCode::CommitIdReuseConflict
214 | ErrorCode::UploadAlreadyCompleted
215 | ErrorCode::UploadContentConflict
216 | ErrorCode::RebootstrapRequired => ErrorKind::Conflict,
217 }
218 }
219}
220
221impl fmt::Display for ErrorCode {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 f.write_str(self.as_str())
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::ErrorCode;
230
231 #[test]
232 fn error_codes_round_trip_through_their_strings() {
233 for code in ErrorCode::ALL {
234 assert_eq!(ErrorCode::parse(code.as_str()), Some(code));
235 }
236 assert_eq!(ErrorCode::parse("not_a_code"), None);
237 }
238
239 #[test]
240 fn error_codes_serde_uses_the_wire_strings() {
241 for code in ErrorCode::ALL {
242 let value = serde_json::to_value(code).expect("serialize error code");
243 assert_eq!(value, serde_json::Value::String(code.as_str().to_owned()));
244 let parsed: ErrorCode = serde_json::from_value(value).expect("deserialize error code");
245 assert_eq!(parsed, code);
246 }
247 assert!(serde_json::from_str::<ErrorCode>("\"not_a_code\"").is_err());
248 }
249}