thin_jsonrpc_client/
raw_response.rs

1use serde_json::value::RawValue;
2use std::borrow::Cow;
3
4/// An error handed back when we cannot correctly deserialize
5/// bytes into our [`Response`] object.
6#[derive(Debug, derive_more::From, derive_more::Display)]
7pub enum ResponseError {
8    /// There was an error deserializing the response.
9    #[from]
10    #[display(fmt = "{error} ({:?})", "std::str::from_utf8(&bytes)")]
11    Deserialize {
12        /// The underlying serde error.
13        error: serde_json::Error,
14        /// The raw bytes that we failed to deserialize.
15        bytes: Vec<u8>
16    },
17    /// The "jsonrpc" field did not equal "2.0".
18    #[display(fmt = "failed to decode response: expected '\"jsonrpc\": \"2.0\"'")]
19    InvalidVersion {
20        /// The raw bytes that we failed to deserialize.
21        bytes: Vec<u8>
22    }
23}
24
25/// A JSON-RPC response is either a "result" or "error" payload.
26/// This represents the shape a message can deserialize into.
27// Dev note: We can't Deserialize things into this in a sane way,
28// because we'd need #[serde(untagged)] + RawValue, which don't play
29// well together.
30#[derive(Clone, Debug, serde::Serialize)]
31#[serde(untagged)]
32pub enum RawResponse<'a> {
33    /// A JSON-RPC "result" response.
34    Ok(OkResponse<'a>),
35    /// A JSON-RPC "error" response.
36    Error(ErrorResponse<'a>)
37}
38
39impl <'a> RawResponse<'a> {
40    /// Return the ID associated with the response, if there is one.
41    /// Notifications from the server that aren't associated with a
42    /// request won't.
43    pub fn id(&self) -> Option<&str> {
44        match self {
45            RawResponse::Ok(r) => r.id.as_deref(),
46            RawResponse::Error(e) => e.id.as_deref()
47        }
48    }
49
50    /// Decode some bytes into a valid JSON-RPC response or
51    /// return an error if it's not valid.
52    pub fn from_bytes(bytes: &[u8]) -> Result<RawResponse<'_>, ResponseError> {
53        let res = match serde_json::from_slice(bytes).map(|res| RawResponse::Ok(res)) {
54            Ok(res) => res,
55            Err(_) => serde_json::from_slice(bytes)
56                .map(|res| RawResponse::Error(res))
57                .map_err(|e| ResponseError::Deserialize { error: e, bytes: bytes.to_owned() })?
58        };
59
60        let version = match &res {
61            RawResponse::Ok(r) => &*r.jsonrpc,
62            RawResponse::Error(e) => &*e.jsonrpc,
63        };
64
65        if version != "2.0" {
66            return Err(ResponseError::InvalidVersion { bytes: bytes.to_owned() })
67        }
68
69        Ok(res)
70    }
71
72    /// Take ownership of a [`Response`], removing any lifetimes.
73    pub fn into_owned(self) -> RawResponse<'static> {
74        match self {
75            RawResponse::Ok(res) => {
76                RawResponse::Ok(res.into_owned())
77            },
78            RawResponse::Error(err) => {
79                RawResponse::Error(err.into_owned())
80            }
81        }
82    }
83
84    /// Construct an "ok" response from an optional ID and serializable "result" value.
85    /// Panics if the value given does not serialize to valid JSON.
86    pub fn ok_from_value<V: serde::Serialize, Id: ToString>(id: Option<Id>, value: V) -> RawResponse<'static> {
87        let value = serde_json::value::to_raw_value(&value).expect("invalid json");
88        RawResponse::Ok(OkResponse {
89            jsonrpc: "2.0".into(),
90            id: id.map(|id| Cow::Owned(id.to_string())),
91            result: Cow::Owned(value)
92        })
93    }
94
95    /// Construct an "error" response from an optional ID and error object.
96    pub fn err_from_value<Id: ToString>(id: Option<Id>, error: ErrorObject<'_>) -> RawResponse<'static> {
97        RawResponse::Error(ErrorResponse {
98            jsonrpc: "2.0".into(),
99            id: id.map(|id| Cow::Owned(id.to_string())),
100            error: error.into_owned()
101        })
102    }
103}
104
105/// A JSON-RPC "result" response.
106#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
107pub struct OkResponse<'a> {
108    /// Always expected to be "2.0".
109    #[serde(borrow)]
110    pub jsonrpc: Cow<'a, str>,
111    /// The message ID. None if this is a server notification.
112    #[serde(borrow)]
113    pub id: Option<Cow<'a, str>>,
114    /// The result body.
115    #[serde(borrow)]
116    pub result: Cow<'a, RawValue>
117}
118
119impl <'a> OkResponse<'a> {
120    /// Take ownership of a [`OkResponse`], removing any lifetimes.
121    pub fn into_owned(self) -> OkResponse<'static> {
122        OkResponse {
123            jsonrpc: Cow::Owned(self.jsonrpc.into_owned()),
124            id: self.id.map(|id| Cow::Owned(id.into_owned())),
125            result: Cow::Owned(self.result.into_owned())
126        }
127    }
128}
129
130/// A JSON-RPC "error" response.
131#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
132pub struct ErrorResponse<'a> {
133    /// Always expected to be "2.0".
134    #[serde(borrow)]
135    pub jsonrpc: Cow<'a, str>,
136    /// The message ID. None if this is a server notification.
137    #[serde(borrow)]
138    pub id: Option<Cow<'a, str>>,
139    /// Error details.
140    #[serde(borrow)]
141    pub error: ErrorObject<'a>
142}
143
144impl <'a> ErrorResponse<'a> {
145    /// Take ownership of a [`ErrorResponse`], removing any lifetimes.
146    pub fn into_owned(self) -> ErrorResponse<'static> {
147        ErrorResponse {
148            jsonrpc: Cow::Owned(self.jsonrpc.into_owned()),
149            id: self.id.map(|id| Cow::Owned(id.into_owned())),
150            error: self.error.into_owned()
151        }
152    }
153}
154
155/// Details about the JSON-RPC error response.
156#[derive(serde::Deserialize, serde::Serialize, derive_more::Display, Clone, Debug)]
157#[display(fmt = "Error {code}: {message}")]
158pub struct ErrorObject<'a> {
159    /// An error code.
160    pub code: ErrorCode,
161    /// A "pretty" error message.
162    #[serde(borrow)]
163    pub message: Cow<'a, str>,
164    /// Some other optional error context.
165    #[serde(borrow)]
166    pub data: Option<Cow<'a, RawValue>>
167}
168
169impl <'a> ErrorObject<'a> {
170    /// Take ownership of a [`ErrorObject`], removing any lifetimes.
171    pub fn into_owned(self) -> ErrorObject<'static> {
172        ErrorObject {
173            code: self.code,
174            message: Cow::Owned(self.message.into_owned()),
175            data: self.data.map(|data| Cow::Owned(data.into_owned()))
176        }
177    }
178}
179
180/// An error code.
181pub type ErrorCode = i32;
182/// Invalid JSON was received by the server.
183pub const CODE_PARSE_ERROR: ErrorCode = -32700;
184/// The JSON send was not a valid request object.
185pub const CODE_INVALID_REQUEST: ErrorCode = -32600;
186/// THe method does not exist/is not available.
187pub const CODE_METHOD_NOT_FOUND: ErrorCode = -32601;
188/// Invalid method parameters.
189pub const CODE_INVALID_PARAMS: ErrorCode = -32602;
190/// Internal server error.
191pub const CODE_INTERNAL_ERROR: ErrorCode = -32603;