aimdb_core/remote/
error.rs1use std::string::String;
4use thiserror::Error;
5
6#[derive(Debug, Clone, Error)]
8pub enum RemoteError {
9 #[error("Protocol error: {message}")]
11 ProtocolError { message: String },
12
13 #[error("Version mismatch: client {client_version}, server {server_version}")]
15 VersionMismatch {
16 client_version: String,
17 server_version: String,
18 },
19
20 #[error("Not found: {resource}")]
22 NotFound { resource: String },
23
24 #[error("Permission denied: {reason}")]
26 PermissionDenied { reason: String },
27
28 #[error("Queue full: {queue_name}")]
30 QueueFull { queue_name: String },
31
32 #[error("Internal error: {message}")]
34 InternalError { message: String },
35
36 #[error("Too many subscriptions (limit: {limit})")]
38 TooManySubscriptions { limit: usize },
39
40 #[error("No value: {record_name}")]
42 NoValue { record_name: String },
43
44 #[error("No buffer: {record_name}")]
46 NoBuffer { record_name: String },
47
48 #[error("Validation error: {message}")]
50 ValidationError { message: String },
51
52 #[error("Authentication required")]
54 AuthRequired,
55
56 #[error("Authentication failed")]
58 AuthFailed,
59}
60
61impl RemoteError {
62 pub fn code(&self) -> &'static str {
64 match self {
65 Self::ProtocolError { .. } => "PROTOCOL_ERROR",
66 Self::VersionMismatch { .. } => "VERSION_MISMATCH",
67 Self::NotFound { .. } => "NOT_FOUND",
68 Self::PermissionDenied { .. } => "PERMISSION_DENIED",
69 Self::QueueFull { .. } => "QUEUE_FULL",
70 Self::InternalError { .. } => "INTERNAL_ERROR",
71 Self::TooManySubscriptions { .. } => "TOO_MANY_SUBSCRIPTIONS",
72 Self::NoValue { .. } => "NO_VALUE",
73 Self::NoBuffer { .. } => "NO_BUFFER",
74 Self::ValidationError { .. } => "VALIDATION_ERROR",
75 Self::AuthRequired => "AUTH_REQUIRED",
76 Self::AuthFailed => "AUTH_FAILED",
77 }
78 }
79
80 pub fn is_retryable(&self) -> bool {
82 matches!(
83 self,
84 Self::NotFound { .. }
85 | Self::QueueFull { .. }
86 | Self::InternalError { .. }
87 | Self::NoValue { .. }
88 )
89 }
90}
91
92pub type RemoteResult<T> = Result<T, RemoteError>;
94
95impl From<crate::DbError> for RemoteError {
97 fn from(err: crate::DbError) -> Self {
98 use crate::DbError;
99 match err {
100 DbError::RecordNotFound { record_name } => RemoteError::NotFound {
101 resource: format!("record '{}'", record_name),
102 },
103 DbError::InvalidOperation { operation, reason } => RemoteError::ValidationError {
104 message: format!("{}: {}", operation, reason),
105 },
106 DbError::BufferFull { buffer_name, .. } => RemoteError::QueueFull {
107 queue_name: buffer_name,
108 },
109 DbError::PermissionDenied { operation } => {
110 RemoteError::PermissionDenied { reason: operation }
111 }
112 _ => RemoteError::InternalError {
113 message: err.to_string(),
114 },
115 }
116 }
117}
118
119impl From<std::io::Error> for RemoteError {
120 fn from(err: std::io::Error) -> Self {
121 RemoteError::InternalError {
122 message: format!("I/O error: {}", err),
123 }
124 }
125}
126
127impl From<serde_json::Error> for RemoteError {
128 fn from(err: serde_json::Error) -> Self {
129 RemoteError::ProtocolError {
130 message: format!("JSON error: {}", err),
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_error_codes() {
141 assert_eq!(
142 RemoteError::ProtocolError {
143 message: "test".to_string(),
144 }
145 .code(),
146 "PROTOCOL_ERROR"
147 );
148
149 assert_eq!(
150 RemoteError::NotFound {
151 resource: "test".to_string(),
152 }
153 .code(),
154 "NOT_FOUND"
155 );
156
157 assert_eq!(
158 RemoteError::PermissionDenied {
159 reason: "test".to_string(),
160 }
161 .code(),
162 "PERMISSION_DENIED"
163 );
164 }
165
166 #[test]
167 fn test_retryable() {
168 assert!(RemoteError::NotFound {
169 resource: "test".to_string(),
170 }
171 .is_retryable());
172
173 assert!(!RemoteError::PermissionDenied {
174 reason: "test".to_string(),
175 }
176 .is_retryable());
177 }
178}