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