1#[derive(Debug, thiserror::Error, PartialEq, Eq)]
5#[non_exhaustive]
6pub enum KrabkaError {
7 #[error("transport: {0}")]
9 Transport(String),
10 #[error("unauthenticated: {0}")]
12 Unauthenticated(String),
13 #[error("invalid argument: {0}")]
15 InvalidArgument(String),
16 #[error("not found: {0}")]
18 NotFound(String),
19 #[error("server error: {0}")]
21 ServerError(String),
22 #[error("{module} is unimplemented; gated on {gated_on}")]
24 Unimplemented {
25 module: &'static str,
27 gated_on: &'static str,
29 },
30}
31
32impl KrabkaError {
33 #[must_use]
35 pub fn from_connect_code(code: &str, message: impl Into<String>) -> Self {
36 let message = message.into();
37 match code {
38 "not_found" => Self::NotFound(message),
39 "invalid_argument" | "failed_precondition" | "out_of_range" => {
40 Self::InvalidArgument(message)
41 }
42 "unauthenticated" => Self::Unauthenticated(message),
43 "unavailable" | "deadline_exceeded" => Self::Transport(message),
44 _ => Self::ServerError(message),
45 }
46 }
47}
48
49impl From<crate::connect_client::ConnectClientError> for KrabkaError {
50 fn from(value: crate::connect_client::ConnectClientError) -> Self {
51 match value {
52 crate::connect_client::ConnectClientError::Connect { code, message } => {
53 Self::from_connect_code(&code, message)
54 }
55 crate::connect_client::ConnectClientError::HttpStatus { status, message } => {
56 match status {
57 400 => Self::InvalidArgument(message),
58 401 => Self::Unauthenticated(message),
59 404 => Self::NotFound(message),
60 408 | 429 | 502..=504 => Self::Transport(message),
61 _ => Self::ServerError(message),
62 }
63 }
64 other => Self::Transport(other.to_string()),
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn connect_codes_map_to_taxonomy() {
75 assert!(matches!(
76 KrabkaError::from_connect_code("not_found", "x"),
77 KrabkaError::NotFound(_)
78 ));
79 assert!(matches!(
80 KrabkaError::from_connect_code("unavailable", "x"),
81 KrabkaError::Transport(_)
82 ));
83 assert!(matches!(
84 KrabkaError::from_connect_code("invalid_argument", "x"),
85 KrabkaError::InvalidArgument(_)
86 ));
87 }
88
89 #[test]
90 fn http_statuses_map_to_taxonomy_when_connect_body_is_unusable() {
91 let invalid = KrabkaError::from(crate::connect_client::ConnectClientError::HttpStatus {
92 status: 400,
93 message: "bad request".into(),
94 });
95 let unavailable =
96 KrabkaError::from(crate::connect_client::ConnectClientError::HttpStatus {
97 status: 503,
98 message: "down".into(),
99 });
100
101 assert2::assert!(let KrabkaError::InvalidArgument(message) = invalid);
102 assert_eq!(message, "bad request");
103 assert2::assert!(let KrabkaError::Transport(message) = unavailable);
104 assert_eq!(message, "down");
105 }
106}