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}