cuprate_json_rpc/error/object.rs
1//! Error object.
2
3//---------------------------------------------------------------------------------------------------- Use
4use std::{borrow::Cow, error::Error, fmt::Display};
5
6use serde::{Deserialize, Serialize};
7use serde_json::value::Value;
8
9use crate::error::{
10 constants::{
11 INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, PARSE_ERROR,
12 SERVER_ERROR,
13 },
14 ErrorCode,
15};
16
17//---------------------------------------------------------------------------------------------------- ErrorObject
18/// [The error object](https://www.jsonrpc.org/specification).
19///
20/// This is the object sent back in a [`Response`](crate::Response)
21/// if the method call errored.
22///
23/// # Display
24/// ```rust
25/// use cuprate_json_rpc::error::ErrorObject;
26///
27/// // The format is `$CODE: $MESSAGE`.
28/// // If a message was not passed during construction,
29/// // the error code's message will be used.
30/// assert_eq!(format!("{}", ErrorObject::parse_error()), "-32700: Parse error");
31/// assert_eq!(format!("{}", ErrorObject::invalid_request()), "-32600: Invalid Request");
32/// assert_eq!(format!("{}", ErrorObject::method_not_found()), "-32601: Method not found");
33/// assert_eq!(format!("{}", ErrorObject::invalid_params()), "-32602: Invalid params");
34/// assert_eq!(format!("{}", ErrorObject::internal_error()), "-32603: Internal error");
35/// assert_eq!(format!("{}", ErrorObject::server_error(0)), "0: Server error");
36///
37/// // Set a custom message.
38/// let mut e = ErrorObject::server_error(1);
39/// e.message = "hello".into();
40/// assert_eq!(format!("{e}"), "1: hello");
41/// ```
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct ErrorObject {
44 /// The error code.
45 pub code: ErrorCode,
46
47 /// A custom message for this error, distinct from [`ErrorCode::msg`].
48 ///
49 /// A JSON `string` value.
50 ///
51 /// This is a `Cow<'static, str>` to support both 0-allocation for
52 /// `const` string ID's commonly found in programs, as well as support
53 /// for runtime [`String`]'s.
54 pub message: Cow<'static, str>,
55
56 /// Optional data associated with the error.
57 ///
58 /// # `None` vs `Some(Value::Null)`
59 /// This field will be completely omitted during serialization if [`None`],
60 /// however if it is `Some(Value::Null)`, it will be serialized as `"data": null`.
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub data: Option<Value>,
63}
64
65impl ErrorObject {
66 /// Creates a new error, deriving the message from the code.
67 ///
68 /// Same as `ErrorObject::from(ErrorCode)`.
69 ///
70 /// ```rust
71 /// use std::borrow::Cow;
72 /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
73 ///
74 /// for code in [
75 /// ErrorCode::ParseError,
76 /// ErrorCode::InvalidRequest,
77 /// ErrorCode::MethodNotFound,
78 /// ErrorCode::InvalidParams,
79 /// ErrorCode::InternalError,
80 /// ErrorCode::ServerError(0),
81 /// ] {
82 /// let object = ErrorObject::from_code(code);
83 /// assert_eq!(object, ErrorObject {
84 /// code,
85 /// message: Cow::Borrowed(code.msg()),
86 /// data: None,
87 /// });
88 ///
89 /// }
90 /// ```
91 pub const fn from_code(code: ErrorCode) -> Self {
92 Self {
93 code,
94 message: Cow::Borrowed(code.msg()),
95 data: None,
96 }
97 }
98
99 /// Creates a new error using [`PARSE_ERROR`].
100 ///
101 /// ```rust
102 /// use std::borrow::Cow;
103 /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
104 ///
105 /// let code = ErrorCode::ParseError;
106 /// let object = ErrorObject::parse_error();
107 /// assert_eq!(object, ErrorObject {
108 /// code,
109 /// message: Cow::Borrowed(code.msg()),
110 /// data: None,
111 /// });
112 /// ```
113 pub const fn parse_error() -> Self {
114 Self {
115 code: ErrorCode::ParseError,
116 message: Cow::Borrowed(PARSE_ERROR.1),
117 data: None,
118 }
119 }
120
121 /// Creates a new error using [`INVALID_REQUEST`].
122 ///
123 /// ```rust
124 /// use std::borrow::Cow;
125 /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
126 ///
127 /// let code = ErrorCode::InvalidRequest;
128 /// let object = ErrorObject::invalid_request();
129 /// assert_eq!(object, ErrorObject {
130 /// code,
131 /// message: Cow::Borrowed(code.msg()),
132 /// data: None,
133 /// });
134 /// ```
135 pub const fn invalid_request() -> Self {
136 Self {
137 code: ErrorCode::InvalidRequest,
138 message: Cow::Borrowed(INVALID_REQUEST.1),
139 data: None,
140 }
141 }
142
143 /// Creates a new error using [`METHOD_NOT_FOUND`].
144 ///
145 /// ```rust
146 /// use std::borrow::Cow;
147 /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
148 ///
149 /// let code = ErrorCode::MethodNotFound;
150 /// let object = ErrorObject::method_not_found();
151 /// assert_eq!(object, ErrorObject {
152 /// code,
153 /// message: Cow::Borrowed(code.msg()),
154 /// data: None,
155 /// });
156 /// ```
157 pub const fn method_not_found() -> Self {
158 Self {
159 code: ErrorCode::MethodNotFound,
160 message: Cow::Borrowed(METHOD_NOT_FOUND.1),
161 data: None,
162 }
163 }
164
165 /// Creates a new error using [`INVALID_PARAMS`].
166 ///
167 /// ```rust
168 /// use std::borrow::Cow;
169 /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
170 ///
171 /// let code = ErrorCode::InvalidParams;
172 /// let object = ErrorObject::invalid_params();
173 /// assert_eq!(object, ErrorObject {
174 /// code,
175 /// message: Cow::Borrowed(code.msg()),
176 /// data: None,
177 /// });
178 /// ```
179 pub const fn invalid_params() -> Self {
180 Self {
181 code: ErrorCode::InvalidParams,
182 message: Cow::Borrowed(INVALID_PARAMS.1),
183 data: None,
184 }
185 }
186
187 /// Creates a new error using [`INTERNAL_ERROR`].
188 ///
189 ///
190 /// ```rust
191 /// use std::borrow::Cow;
192 /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
193 ///
194 /// let code = ErrorCode::InternalError;
195 /// let object = ErrorObject::internal_error();
196 /// assert_eq!(object, ErrorObject {
197 /// code,
198 /// message: Cow::Borrowed(code.msg()),
199 /// data: None,
200 /// });
201 /// ```
202 pub const fn internal_error() -> Self {
203 Self {
204 code: ErrorCode::InternalError,
205 message: Cow::Borrowed(INTERNAL_ERROR.1),
206 data: None,
207 }
208 }
209
210 /// Creates a new error using [`SERVER_ERROR`].
211 ///
212 /// You must provide the custom [`i32`] error code.
213 ///
214 /// ```rust
215 /// use std::borrow::Cow;
216 /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
217 ///
218 /// let code = ErrorCode::ServerError(0);
219 /// let object = ErrorObject::server_error(0);
220 /// assert_eq!(object, ErrorObject {
221 /// code,
222 /// message: Cow::Borrowed(code.msg()),
223 /// data: None,
224 /// });
225 /// ```
226 pub const fn server_error(error_code: i32) -> Self {
227 Self {
228 code: ErrorCode::ServerError(error_code),
229 message: Cow::Borrowed(SERVER_ERROR),
230 data: None,
231 }
232 }
233}
234
235//---------------------------------------------------------------------------------------------------- Trait impl
236impl From<ErrorCode> for ErrorObject {
237 fn from(code: ErrorCode) -> Self {
238 Self::from_code(code)
239 }
240}
241
242impl Display for ErrorObject {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 // Using `self.code`'s formatting will write the
245 // message twice, so prefer the built-in message.
246 write!(f, "{}: {}", self.code.code(), self.message)
247 }
248}
249
250impl Error for ErrorObject {
251 fn source(&self) -> Option<&(dyn Error + 'static)> {
252 Some(&self.code)
253 }
254
255 fn description(&self) -> &str {
256 &self.message
257 }
258}