jsonrpsee_types/
error.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26
27use std::fmt;
28
29use serde::de::Deserializer;
30use serde::ser::Serializer;
31use serde::{Deserialize, Serialize};
32use serde_json::value::RawValue;
33use std::borrow::{Borrow, Cow as StdCow};
34use thiserror::Error;
35
36/// Owned variant of [`ErrorObject`].
37pub type ErrorObjectOwned = ErrorObject<'static>;
38
39/// [Failed JSON-RPC response object](https://www.jsonrpc.org/specification#response_object).
40#[derive(Debug, Deserialize, Serialize, Clone, thiserror::Error)]
41#[serde(deny_unknown_fields)]
42#[error("{self:?}")]
43pub struct ErrorObject<'a> {
44	/// Code
45	code: ErrorCode,
46	/// Message
47	message: StdCow<'a, str>,
48	/// Optional data
49	#[serde(skip_serializing_if = "Option::is_none")]
50	data: Option<StdCow<'a, RawValue>>,
51}
52
53impl<'a> ErrorObject<'a> {
54	/// Return the error code
55	pub fn code(&self) -> i32 {
56		self.code.code()
57	}
58
59	/// Return the message
60	pub fn message(&self) -> &str {
61		self.message.borrow()
62	}
63
64	/// Return the data associated with this error, if any
65	pub fn data(&self) -> Option<&RawValue> {
66		self.data.as_ref().map(|d| d.borrow())
67	}
68
69	/// Create a new `ErrorObjectOwned` with optional data.
70	pub fn owned<S: Serialize>(code: i32, message: impl Into<String>, data: Option<S>) -> ErrorObject<'static> {
71		let data = data.and_then(|d| serde_json::value::to_raw_value(&d).ok());
72		ErrorObject { code: code.into(), message: message.into().into(), data: data.map(StdCow::Owned) }
73	}
74
75	/// Create a new [`ErrorObject`] with optional data.
76	pub fn borrowed(code: i32, message: &'a str, data: Option<&'a RawValue>) -> ErrorObject<'a> {
77		ErrorObject { code: code.into(), message: StdCow::Borrowed(message), data: data.map(StdCow::Borrowed) }
78	}
79
80	/// Take ownership of the parameters within, if we haven't already.
81	pub fn into_owned(self) -> ErrorObject<'static> {
82		ErrorObject {
83			code: self.code,
84			message: StdCow::Owned(self.message.into_owned()),
85			data: self.data.map(|d| StdCow::Owned(d.into_owned())),
86		}
87	}
88
89	/// Borrow the current [`ErrorObject`].
90	pub fn borrow(&'a self) -> ErrorObject<'a> {
91		ErrorObject {
92			code: self.code,
93			message: StdCow::Borrowed(self.message.borrow()),
94			data: self.data.as_ref().map(|d| StdCow::Borrowed(d.borrow())),
95		}
96	}
97}
98
99impl<'a> PartialEq for ErrorObject<'a> {
100	fn eq(&self, other: &Self) -> bool {
101		let this_raw = self.data.as_ref().map(|r| r.get());
102		let other_raw = other.data.as_ref().map(|r| r.get());
103		self.code == other.code && self.message == other.message && this_raw == other_raw
104	}
105}
106
107impl<'a> From<ErrorCode> for ErrorObject<'a> {
108	fn from(code: ErrorCode) -> Self {
109		Self { code, message: code.message().into(), data: None }
110	}
111}
112
113/// Parse error code.
114pub const PARSE_ERROR_CODE: i32 = -32700;
115/// Invalid request error code.
116pub const INVALID_REQUEST_CODE: i32 = -32600;
117/// Method not found error code.
118pub const METHOD_NOT_FOUND_CODE: i32 = -32601;
119/// Invalid params error code.
120pub const INVALID_PARAMS_CODE: i32 = -32602;
121/// Internal error code.
122pub const INTERNAL_ERROR_CODE: i32 = -32603;
123/// Custom server error when a call failed.
124pub const CALL_EXECUTION_FAILED_CODE: i32 = -32000;
125/// Unknown error.
126pub const UNKNOWN_ERROR_CODE: i32 = -32001;
127/// Batched requests are not supported by the server.
128pub const BATCHES_NOT_SUPPORTED_CODE: i32 = -32005;
129/// Subscription limit per connection was exceeded.
130pub const TOO_MANY_SUBSCRIPTIONS_CODE: i32 = -32006;
131/// Oversized request error code.
132pub const OVERSIZED_REQUEST_CODE: i32 = -32007;
133/// Oversized response error code.
134pub const OVERSIZED_RESPONSE_CODE: i32 = -32008;
135/// Server is busy error code.
136pub const SERVER_IS_BUSY_CODE: i32 = -32009;
137/// Batch request limit was exceed.
138pub const TOO_BIG_BATCH_REQUEST_CODE: i32 = -32010;
139/// Batch request limit was exceed.
140pub const TOO_BIG_BATCH_RESPONSE_CODE: i32 = -32011;
141
142/// Parse error message
143pub const PARSE_ERROR_MSG: &str = "Parse error";
144/// Oversized request message
145pub const OVERSIZED_REQUEST_MSG: &str = "Request is too big";
146/// Oversized response message
147pub const OVERSIZED_RESPONSE_MSG: &str = "Response is too big";
148/// Internal error message.
149pub const INTERNAL_ERROR_MSG: &str = "Internal error";
150/// Invalid params error message.
151pub const INVALID_PARAMS_MSG: &str = "Invalid params";
152/// Invalid request error message.
153pub const INVALID_REQUEST_MSG: &str = "Invalid request";
154/// Method not found error message.
155pub const METHOD_NOT_FOUND_MSG: &str = "Method not found";
156/// Server is busy error message.
157pub const SERVER_IS_BUSY_MSG: &str = "Server is busy, try again later";
158/// Reserved for implementation-defined server-errors.
159pub const SERVER_ERROR_MSG: &str = "Server error";
160/// Batched requests not supported error message.
161pub const BATCHES_NOT_SUPPORTED_MSG: &str = "Batched requests are not supported by this server";
162/// Subscription limit per connection was exceeded.
163pub const TOO_MANY_SUBSCRIPTIONS_MSG: &str = "Too many subscriptions on the connection";
164/// Batched requests limit was exceed.
165pub const TOO_BIG_BATCH_REQUEST_MSG: &str = "The batch request was too large";
166/// Batch request response limit was exceed.
167pub const TOO_BIG_BATCH_RESPONSE_MSG: &str = "The batch response was too large";
168
169/// JSONRPC error code
170#[derive(Error, Debug, PartialEq, Eq, Copy, Clone)]
171pub enum ErrorCode {
172	/// Invalid JSON was received by the server.
173	/// An error occurred on the server while parsing the JSON text.
174	ParseError,
175	/// The request was too big.
176	OversizedRequest,
177	/// The JSON sent is not a valid Request object.
178	InvalidRequest,
179	/// The method does not exist / is not available.
180	MethodNotFound,
181	/// Server is busy / resources are at capacity.
182	ServerIsBusy,
183	/// Invalid method parameter(s).
184	InvalidParams,
185	/// Internal JSON-RPC error.
186	InternalError,
187	/// Reserved for implementation-defined server-errors.
188	ServerError(i32),
189}
190
191impl ErrorCode {
192	/// Returns integer code value
193	pub const fn code(&self) -> i32 {
194		use ErrorCode::*;
195		match *self {
196			ParseError => PARSE_ERROR_CODE,
197			OversizedRequest => OVERSIZED_REQUEST_CODE,
198			InvalidRequest => INVALID_REQUEST_CODE,
199			MethodNotFound => METHOD_NOT_FOUND_CODE,
200			ServerIsBusy => SERVER_IS_BUSY_CODE,
201			InvalidParams => INVALID_PARAMS_CODE,
202			InternalError => INTERNAL_ERROR_CODE,
203			ServerError(code) => code,
204		}
205	}
206
207	/// Returns the message for the given error code.
208	pub const fn message(&self) -> &'static str {
209		use ErrorCode::*;
210		match self {
211			ParseError => PARSE_ERROR_MSG,
212			OversizedRequest => OVERSIZED_REQUEST_MSG,
213			InvalidRequest => INVALID_REQUEST_MSG,
214			MethodNotFound => METHOD_NOT_FOUND_MSG,
215			ServerIsBusy => SERVER_IS_BUSY_MSG,
216			InvalidParams => INVALID_PARAMS_MSG,
217			InternalError => INTERNAL_ERROR_MSG,
218			ServerError(_) => SERVER_ERROR_MSG,
219		}
220	}
221}
222
223impl fmt::Display for ErrorCode {
224	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225		write!(f, "{}: {}", self.code(), self.message())
226	}
227}
228
229impl From<i32> for ErrorCode {
230	fn from(code: i32) -> Self {
231		use ErrorCode::*;
232		match code {
233			PARSE_ERROR_CODE => ParseError,
234			OVERSIZED_REQUEST_CODE => OversizedRequest,
235			INVALID_REQUEST_CODE => InvalidRequest,
236			METHOD_NOT_FOUND_CODE => MethodNotFound,
237			INVALID_PARAMS_CODE => InvalidParams,
238			INTERNAL_ERROR_CODE => InternalError,
239			code => ServerError(code),
240		}
241	}
242}
243
244impl<'a> serde::Deserialize<'a> for ErrorCode {
245	fn deserialize<D>(deserializer: D) -> Result<ErrorCode, D::Error>
246	where
247		D: Deserializer<'a>,
248	{
249		let code: i32 = Deserialize::deserialize(deserializer)?;
250		Ok(ErrorCode::from(code))
251	}
252}
253
254impl serde::Serialize for ErrorCode {
255	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
256	where
257		S: Serializer,
258	{
259		serializer.serialize_i32(self.code())
260	}
261}
262
263/// Helper to get a `JSON-RPC` error object when the maximum number of subscriptions have been exceeded.
264pub fn reject_too_many_subscriptions(limit: u32) -> ErrorObjectOwned {
265	ErrorObjectOwned::owned(
266		TOO_MANY_SUBSCRIPTIONS_CODE,
267		TOO_MANY_SUBSCRIPTIONS_MSG,
268		Some(format!("Exceeded max limit of {limit}")),
269	)
270}
271
272/// Helper to get a `JSON-RPC` error object when the maximum request size limit have been exceeded.
273pub fn reject_too_big_request(limit: u32) -> ErrorObjectOwned {
274	ErrorObjectOwned::owned(
275		OVERSIZED_REQUEST_CODE,
276		OVERSIZED_REQUEST_MSG,
277		Some(format!("Exceeded max limit of {limit}")),
278	)
279}
280
281/// Helper to get a `JSON-RPC` error object when the maximum batch request size have been exceeded.
282pub fn reject_too_big_batch_request(limit: usize) -> ErrorObjectOwned {
283	ErrorObjectOwned::owned(
284		TOO_BIG_BATCH_REQUEST_CODE,
285		TOO_BIG_BATCH_REQUEST_MSG,
286		Some(format!("Exceeded max limit of {limit}")),
287	)
288}
289
290/// Helper to get a `JSON-RPC` error object when the maximum batch response size have been exceeded.
291pub fn reject_too_big_batch_response(limit: usize) -> ErrorObjectOwned {
292	ErrorObjectOwned::owned(
293		TOO_BIG_BATCH_RESPONSE_CODE,
294		TOO_BIG_BATCH_RESPONSE_MSG,
295		Some(format!("Exceeded max limit of {limit}")),
296	)
297}
298
299#[cfg(test)]
300mod tests {
301	use super::{ErrorCode, ErrorObject};
302
303	#[test]
304	fn deserialize_works() {
305		let ser = r#"{"code":-32700,"message":"Parse error"}"#;
306		let exp: ErrorObject = ErrorCode::ParseError.into();
307		let err: ErrorObject = serde_json::from_str(ser).unwrap();
308		assert_eq!(exp, err);
309	}
310
311	#[test]
312	fn deserialize_with_optional_data() {
313		let ser = r#"{"code":-32700,"message":"Parse error", "data":"vegan"}"#;
314		let data = serde_json::value::to_raw_value(&"vegan").unwrap();
315		let exp = ErrorObject::owned(ErrorCode::ParseError.code(), "Parse error", Some(data));
316		let err: ErrorObject = serde_json::from_str(ser).unwrap();
317		assert_eq!(exp, err);
318	}
319
320	#[test]
321	fn deserialized_error_with_quoted_str() {
322		let raw = r#"{
323				"code": 1002,
324				"message": "desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })",
325				"data": "\\\"validate_transaction\\\""
326		}"#;
327		let err: ErrorObject = serde_json::from_str(raw).unwrap();
328
329		let data = serde_json::value::to_raw_value(&"\\\"validate_transaction\\\"").unwrap();
330		let exp = ErrorObject::borrowed(
331			1002,
332			"desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })",
333			Some(&*data),
334		);
335
336		assert_eq!(err, exp);
337	}
338
339	#[test]
340	fn serialize_works() {
341		let exp = r#"{"code":-32603,"message":"Internal error"}"#;
342		let err: ErrorObject = ErrorCode::InternalError.into();
343		let ser = serde_json::to_string(&err).unwrap();
344		assert_eq!(exp, ser);
345	}
346
347	#[test]
348	fn serialize_optional_data_works() {
349		let exp = r#"{"code":-32699,"message":"food","data":"not vegan"}"#;
350		let data = serde_json::value::to_raw_value(&"not vegan").unwrap();
351		let ser = serde_json::to_string(&ErrorObject::owned(-32699, "food", Some(data))).unwrap();
352		assert_eq!(exp, ser);
353	}
354}