1use std::{borrow::Cow, fmt::Debug, hash::Hash};
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use tracing::{error, warn};
6
7pub trait ErrorCodeT:
45 Into<(i64, &'static str)> + for<'de> Deserialize<'de> + Copy + Eq + Debug
46{
47 #[doc(hidden)]
51 fn is_reserved() -> bool {
52 false
53 }
54}
55
56#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, Debug)]
61#[repr(i64)]
62pub enum ReservedErrorCode {
63 ParseError = -32700,
65 InvalidRequest = -32600,
67 MethodNotFound = -32601,
69 InvalidParams = -32602,
71 InternalError = -32603,
73}
74
75impl From<ReservedErrorCode> for (i64, &'static str) {
76 fn from(error_code: ReservedErrorCode) -> Self {
77 match error_code {
78 ReservedErrorCode::ParseError => (error_code as i64, "Parse error"),
79 ReservedErrorCode::InvalidRequest => (error_code as i64, "Invalid Request"),
80 ReservedErrorCode::MethodNotFound => (error_code as i64, "Method not found"),
81 ReservedErrorCode::InvalidParams => (error_code as i64, "Invalid params"),
82 ReservedErrorCode::InternalError => (error_code as i64, "Internal error"),
83 }
84 }
85}
86
87impl ErrorCodeT for ReservedErrorCode {
88 fn is_reserved() -> bool {
89 true
90 }
91}
92
93#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
98#[serde(deny_unknown_fields)]
99pub struct Error {
100 code: i64,
102 message: Cow<'static, str>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 data: Option<Value>,
107}
108
109impl Error {
110 pub fn new<C: ErrorCodeT, T: Serialize>(error_code: C, additional_info: T) -> Self {
126 let (code, message): (i64, &'static str) = error_code.into();
127
128 if !C::is_reserved() && (-32768..=-32100).contains(&code) {
129 warn!(%code, "provided json-rpc error code is reserved; returning internal error");
130 let (code, message) = ReservedErrorCode::InternalError.into();
131 return Error {
132 code,
133 message: Cow::Borrowed(message),
134 data: Some(Value::String(format!(
135 "attempted to return reserved error code {}",
136 code
137 ))),
138 };
139 }
140
141 let data = match serde_json::to_value(additional_info) {
142 Ok(Value::Null) => None,
143 Ok(value) => Some(value),
144 Err(error) => {
145 error!(%error, "failed to json-encode additional info in json-rpc error");
146 let (code, message) = ReservedErrorCode::InternalError.into();
147 return Error {
148 code,
149 message: Cow::Borrowed(message),
150 data: Some(Value::String(format!(
151 "failed to json-encode additional info in json-rpc error: {}",
152 error
153 ))),
154 };
155 }
156 };
157
158 Error {
159 code,
160 message: Cow::Borrowed(message),
161 data,
162 }
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use serde::ser::{Error as _, Serializer};
169
170 use super::*;
171
172 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, Debug)]
173 struct TestErrorCode {
174 in_reserved_range: bool,
176 }
177
178 impl From<TestErrorCode> for (i64, &'static str) {
179 fn from(error_code: TestErrorCode) -> Self {
180 if error_code.in_reserved_range {
181 (-32768, "Invalid test error")
182 } else {
183 (-123, "Valid test error")
184 }
185 }
186 }
187
188 impl ErrorCodeT for TestErrorCode {}
189
190 #[derive(Serialize)]
191 struct AdditionalInfo {
192 id: u64,
193 context: &'static str,
194 }
195
196 impl Default for AdditionalInfo {
197 fn default() -> Self {
198 AdditionalInfo {
199 id: 1314,
200 context: "TEST",
201 }
202 }
203 }
204
205 struct FailToEncode;
206
207 impl Serialize for FailToEncode {
208 fn serialize<S: Serializer>(&self, _serializer: S) -> Result<S::Ok, S::Error> {
209 Err(S::Error::custom("won't encode"))
210 }
211 }
212
213 #[test]
214 fn should_construct_reserved_error() {
215 const EXPECTED_WITH_DATA: &str =
216 r#"{"code":-32700,"message":"Parse error","data":{"id":1314,"context":"TEST"}}"#;
217 const EXPECTED_WITHOUT_DATA: &str = r#"{"code":-32601,"message":"Method not found"}"#;
218 const EXPECTED_WITH_BAD_DATA: &str = r#"{"code":-32603,"message":"Internal error","data":"failed to json-encode additional info in json-rpc error: won't encode"}"#;
219
220 let error_with_data = Error::new(ReservedErrorCode::ParseError, AdditionalInfo::default());
221 let encoded = serde_json::to_string(&error_with_data).unwrap();
222 assert_eq!(encoded, EXPECTED_WITH_DATA);
223
224 let error_without_data = Error::new(ReservedErrorCode::MethodNotFound, None::<u8>);
225 let encoded = serde_json::to_string(&error_without_data).unwrap();
226 assert_eq!(encoded, EXPECTED_WITHOUT_DATA);
227
228 let error_with_bad_data = Error::new(ReservedErrorCode::InvalidParams, FailToEncode);
229 let encoded = serde_json::to_string(&error_with_bad_data).unwrap();
230 assert_eq!(encoded, EXPECTED_WITH_BAD_DATA);
231 }
232
233 #[test]
234 fn should_construct_custom_error() {
235 const EXPECTED_WITH_DATA: &str =
236 r#"{"code":-123,"message":"Valid test error","data":{"id":1314,"context":"TEST"}}"#;
237 const EXPECTED_WITHOUT_DATA: &str = r#"{"code":-123,"message":"Valid test error"}"#;
238 const EXPECTED_WITH_BAD_DATA: &str = r#"{"code":-32603,"message":"Internal error","data":"failed to json-encode additional info in json-rpc error: won't encode"}"#;
239
240 let good_error_code = TestErrorCode {
241 in_reserved_range: false,
242 };
243
244 let error_with_data = Error::new(good_error_code, AdditionalInfo::default());
245 let encoded = serde_json::to_string(&error_with_data).unwrap();
246 assert_eq!(encoded, EXPECTED_WITH_DATA);
247
248 let error_without_data = Error::new(good_error_code, ());
249 let encoded = serde_json::to_string(&error_without_data).unwrap();
250 assert_eq!(encoded, EXPECTED_WITHOUT_DATA);
251
252 let error_with_bad_data = Error::new(good_error_code, FailToEncode);
253 let encoded = serde_json::to_string(&error_with_bad_data).unwrap();
254 assert_eq!(encoded, EXPECTED_WITH_BAD_DATA);
255 }
256
257 #[test]
258 fn should_fall_back_to_internal_error_on_bad_custom_error() {
259 const EXPECTED: &str = r#"{"code":-32603,"message":"Internal error","data":"attempted to return reserved error code -32603"}"#;
260
261 let bad_error_code = TestErrorCode {
262 in_reserved_range: true,
263 };
264
265 let error_with_data = Error::new(bad_error_code, AdditionalInfo::default());
266 let encoded = serde_json::to_string(&error_with_data).unwrap();
267 assert_eq!(encoded, EXPECTED);
268
269 let error_without_data = Error::new(bad_error_code, None::<u8>);
270 let encoded = serde_json::to_string(&error_without_data).unwrap();
271 assert_eq!(encoded, EXPECTED);
272
273 let error_with_bad_data = Error::new(bad_error_code, FailToEncode);
274 let encoded = serde_json::to_string(&error_with_bad_data).unwrap();
275 assert_eq!(encoded, EXPECTED);
276 }
277}