xi_rpc/error.rs
1// Copyright 2017 The xi-editor Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16use std::io;
17
18use serde::de::{Deserialize, Deserializer};
19use serde::ser::{Serialize, Serializer};
20use serde_json::{Error as JsonError, Value};
21
22/// The possible error outcomes when attempting to send a message.
23#[derive(Debug)]
24pub enum Error {
25 /// An IO error occurred on the underlying communication channel.
26 Io(io::Error),
27 /// The peer returned an error.
28 RemoteError(RemoteError),
29 /// The peer closed the connection.
30 PeerDisconnect,
31 /// The peer sent a response containing the id, but was malformed.
32 InvalidResponse,
33}
34
35/// The possible error outcomes when attempting to read a message.
36#[derive(Debug)]
37pub enum ReadError {
38 /// An error occurred in the underlying stream
39 Io(io::Error),
40 /// The message was not valid JSON.
41 Json(JsonError),
42 /// The message was not a JSON object.
43 NotObject,
44 /// The the method and params were not recognized by the handler.
45 UnknownRequest(JsonError),
46 /// The peer closed the connection.
47 Disconnect,
48}
49
50/// Errors that can be received from the other side of the RPC channel.
51///
52/// This type is intended to go over the wire. And by convention
53/// should `Serialize` as a JSON object with "code", "message",
54/// and optionally "data" fields.
55///
56/// The xi RPC protocol defines one error: `RemoteError::InvalidRequest`,
57/// represented by error code `-32600`; however codes in the range
58/// `-32700 ... -32000` (inclusive) are reserved for compatability with
59/// the JSON-RPC spec.
60///
61/// # Examples
62///
63/// An invalid request:
64///
65/// ```
66/// # extern crate xi_rpc;
67/// # extern crate serde_json;
68/// # fn main() {
69/// use xi_rpc::RemoteError;
70/// use serde_json::Value;
71///
72/// let json = r#"{
73/// "code": -32600,
74/// "message": "Invalid request",
75/// "data": "Additional details"
76/// }"#;
77///
78/// let err = serde_json::from_str::<RemoteError>(&json).unwrap();
79/// assert_eq!(err,
80/// RemoteError::InvalidRequest(
81/// Some(Value::String("Additional details".into()))));
82/// # }
83/// ```
84///
85/// A custom error:
86///
87/// ```
88/// # extern crate xi_rpc;
89/// # extern crate serde_json;
90/// # fn main() {
91/// use xi_rpc::RemoteError;
92/// use serde_json::Value;
93///
94/// let json = r#"{
95/// "code": 404,
96/// "message": "Not Found"
97/// }"#;
98///
99/// let err = serde_json::from_str::<RemoteError>(&json).unwrap();
100/// assert_eq!(err, RemoteError::custom(404, "Not Found", None));
101/// # }
102/// ```
103#[derive(Debug, Clone, PartialEq)]
104pub enum RemoteError {
105 /// The JSON was valid, but was not a correctly formed request.
106 ///
107 /// This Error is used internally, and should not be returned by
108 /// clients.
109 InvalidRequest(Option<Value>),
110 /// A custom error, defined by the client.
111 Custom { code: i64, message: String, data: Option<Value> },
112 /// An error that cannot be represented by an error object.
113 ///
114 /// This error is intended to accommodate clients that return arbitrary
115 /// error values. It should not be used for new errors.
116 Unknown(Value),
117}
118
119impl RemoteError {
120 /// Creates a new custom error.
121 pub fn custom<S, V>(code: i64, message: S, data: V) -> Self
122 where
123 S: AsRef<str>,
124 V: Into<Option<Value>>,
125 {
126 let message = message.as_ref().into();
127 let data = data.into();
128 RemoteError::Custom { code, message, data }
129 }
130}
131
132impl ReadError {
133 /// Returns `true` iff this is the `ReadError::Disconnect` variant.
134 pub fn is_disconnect(&self) -> bool {
135 match *self {
136 ReadError::Disconnect => true,
137 _ => false,
138 }
139 }
140}
141
142impl fmt::Display for ReadError {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 match *self {
145 ReadError::Io(ref err) => write!(f, "I/O Error: {:?}", err),
146 ReadError::Json(ref err) => write!(f, "JSON Error: {:?}", err),
147 ReadError::NotObject => write!(f, "JSON message was not an object."),
148 ReadError::UnknownRequest(ref err) => write!(f, "Unknown request: {:?}", err),
149 ReadError::Disconnect => write!(f, "Peer closed the connection."),
150 }
151 }
152}
153
154impl From<JsonError> for ReadError {
155 fn from(err: JsonError) -> ReadError {
156 ReadError::Json(err)
157 }
158}
159
160impl From<io::Error> for ReadError {
161 fn from(err: io::Error) -> ReadError {
162 ReadError::Io(err)
163 }
164}
165
166impl From<JsonError> for RemoteError {
167 fn from(err: JsonError) -> RemoteError {
168 RemoteError::InvalidRequest(Some(json!(err.to_string())))
169 }
170}
171
172impl From<RemoteError> for Error {
173 fn from(err: RemoteError) -> Error {
174 Error::RemoteError(err)
175 }
176}
177
178#[derive(Deserialize, Serialize)]
179struct ErrorHelper {
180 code: i64,
181 message: String,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 data: Option<Value>,
184}
185
186impl<'de> Deserialize<'de> for RemoteError {
187 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
188 where
189 D: Deserializer<'de>,
190 {
191 let v = Value::deserialize(deserializer)?;
192 let resp = match ErrorHelper::deserialize(&v) {
193 Ok(resp) => resp,
194 Err(_) => return Ok(RemoteError::Unknown(v)),
195 };
196
197 Ok(match resp.code {
198 -32600 => RemoteError::InvalidRequest(resp.data),
199 _ => RemoteError::Custom { code: resp.code, message: resp.message, data: resp.data },
200 })
201 }
202}
203
204impl Serialize for RemoteError {
205 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
206 where
207 S: Serializer,
208 {
209 let (code, message, data) = match *self {
210 RemoteError::InvalidRequest(ref d) => (-32600, "Invalid request", d),
211 RemoteError::Custom { code, ref message, ref data } => (code, message.as_ref(), data),
212 RemoteError::Unknown(_) => panic!(
213 "The 'Unknown' error variant is \
214 not intended for client use."
215 ),
216 };
217 let message = message.to_owned();
218 let data = data.to_owned();
219 let err = ErrorHelper { code, message, data };
220 err.serialize(serializer)
221 }
222}