1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum ErrorCode {
8 BadRequest,
9 NotFound,
10 CapRevoked,
11 PermissionDenied,
12 Canceled,
13 Internal,
14}
15
16impl fmt::Display for ErrorCode {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 let s = match self {
19 ErrorCode::BadRequest => "bad_request",
20 ErrorCode::NotFound => "not_found",
21 ErrorCode::CapRevoked => "cap_revoked",
22 ErrorCode::PermissionDenied => "permission_denied",
23 ErrorCode::Canceled => "canceled",
24 ErrorCode::Internal => "internal",
25 };
26 write!(f, "{}", s)
27 }
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31pub struct RpcError {
32 pub code: ErrorCode,
33 pub message: String,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub data: Option<Value>,
36}
37
38impl RpcError {
39 pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
40 RpcError {
41 code,
42 message: message.into(),
43 data: None,
44 }
45 }
46
47 pub fn with_data(code: ErrorCode, message: impl Into<String>, data: Value) -> Self {
48 RpcError {
49 code,
50 message: message.into(),
51 data: Some(data),
52 }
53 }
54
55 pub fn bad_request(message: impl Into<String>) -> Self {
56 Self::new(ErrorCode::BadRequest, message)
57 }
58
59 pub fn not_found(message: impl Into<String>) -> Self {
60 Self::new(ErrorCode::NotFound, message)
61 }
62
63 pub fn cap_revoked(message: impl Into<String>) -> Self {
64 Self::new(ErrorCode::CapRevoked, message)
65 }
66
67 pub fn permission_denied(message: impl Into<String>) -> Self {
68 Self::new(ErrorCode::PermissionDenied, message)
69 }
70
71 pub fn canceled(message: impl Into<String>) -> Self {
72 Self::new(ErrorCode::Canceled, message)
73 }
74
75 pub fn internal(message: impl Into<String>) -> Self {
76 Self::new(ErrorCode::Internal, message)
77 }
78}
79
80impl fmt::Display for RpcError {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{:?}: {}", self.code, self.message)
83 }
84}
85
86impl std::error::Error for RpcError {}
87
88impl From<serde_json::Error> for RpcError {
89 fn from(err: serde_json::Error) -> Self {
90 RpcError::bad_request(format!("JSON error: {}", err))
91 }
92}
93
94impl From<std::io::Error> for RpcError {
95 fn from(err: std::io::Error) -> Self {
96 RpcError::internal(format!("IO error: {}", err))
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_error_creation() {
106 let err = RpcError::new(ErrorCode::BadRequest, "Invalid input");
107 assert_eq!(err.code, ErrorCode::BadRequest);
108 assert_eq!(err.message, "Invalid input");
109 assert_eq!(err.data, None);
110 }
111
112 #[test]
113 fn test_error_with_data() {
114 let data = serde_json::json!({"field": "value"});
115 let err = RpcError::with_data(ErrorCode::Internal, "Server error", data.clone());
116 assert_eq!(err.code, ErrorCode::Internal);
117 assert_eq!(err.message, "Server error");
118 assert_eq!(err.data, Some(data));
119 }
120
121 #[test]
122 fn test_convenience_constructors() {
123 let err = RpcError::bad_request("Bad input");
124 assert_eq!(err.code, ErrorCode::BadRequest);
125
126 let err = RpcError::not_found("Resource not found");
127 assert_eq!(err.code, ErrorCode::NotFound);
128
129 let err = RpcError::cap_revoked("Capability revoked");
130 assert_eq!(err.code, ErrorCode::CapRevoked);
131
132 let err = RpcError::permission_denied("Access denied");
133 assert_eq!(err.code, ErrorCode::PermissionDenied);
134
135 let err = RpcError::canceled("Operation canceled");
136 assert_eq!(err.code, ErrorCode::Canceled);
137
138 let err = RpcError::internal("Internal error");
139 assert_eq!(err.code, ErrorCode::Internal);
140 }
141
142 #[test]
143 fn test_error_serialization() {
144 let err = RpcError::new(ErrorCode::NotFound, "Resource not found");
145 let json = serde_json::to_string(&err).unwrap();
146 let deserialized: RpcError = serde_json::from_str(&json).unwrap();
147 assert_eq!(err, deserialized);
148 }
149
150 #[test]
151 fn test_error_serialization_with_data() {
152 let data = serde_json::json!({"id": 123});
153 let err = RpcError::with_data(ErrorCode::BadRequest, "Invalid ID", data);
154 let json = serde_json::to_string(&err).unwrap();
155 assert!(json.contains("\"data\""));
156 let deserialized: RpcError = serde_json::from_str(&json).unwrap();
157 assert_eq!(err, deserialized);
158 }
159
160 #[test]
161 fn test_error_display() {
162 let err = RpcError::internal("Something went wrong");
163 let display = format!("{}", err);
164 assert!(display.contains("Internal"));
165 assert!(display.contains("Something went wrong"));
166 }
167}