cuprate-hinto-janai/rpc/json-rpc/src/response.rs
hinto-janai eead49beb0
lints: opt in manual lint crates (#263)
* cargo.toml: transfer existing lints

* rpc/interface: lints

* rpc/json-rpc: lints

* rpc/types: lints

* storage/blockchain: lints

* rpc/types: fix lints

* cargo.toml: fix lint group priority

* storage/blockchain: fix lints

* fix misc lints

* storage/database: fixes

* storage/txpool: opt in lints + fixes

* types: opt in + fixes

* helper: opt in + fixes

* types: remove borsh

* rpc/interface: fix test

* test fixes

* database: fix lints

* fix lint

* tabs -> spaces

* blockchain: `config/` -> `config.rs`
2024-09-02 18:12:54 +01:00

485 lines
17 KiB
Rust

//! JSON-RPC 2.0 response object.
//---------------------------------------------------------------------------------------------------- Use
use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use crate::{error::ErrorObject, id::Id, version::Version};
//---------------------------------------------------------------------------------------------------- Response
/// [The response object](https://www.jsonrpc.org/specification#response_object).
///
/// The generic `T` is the response payload, i.e. it is the
/// type that holds both the `method` and `params`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response<T> {
/// JSON-RPC protocol version; always `2.0`.
pub jsonrpc: Version,
/// This field must always be present in serialized JSON.
///
/// ### JSON-RPC 2.0 rules
/// - The [`Response`]'s ID must be the same as the [`Request`](crate::Request)
/// - If the `Request` omitted the `id` field, there should be no `Response`
/// - If there was an error in detecting the `Request`'s ID, the `Response` must contain an [`Id::Null`]
pub id: Id,
/// The response payload.
///
/// ### JSON-RPC 2.0 rules
/// - This must be [`Ok`] upon success
/// - This must be [`Err`] upon error
/// - This can be any (de)serializable data `T` on success
/// - This must be [`ErrorObject`] on errors
pub payload: Result<T, ErrorObject>,
}
impl<T> Response<T> {
/// Creates a successful response.
///
/// ```rust
/// use cuprate_json_rpc::{Id, Response};
///
/// let ok = Response::ok(Id::Num(123), "OK");
/// let json = serde_json::to_string(&ok).unwrap();
/// assert_eq!(json, r#"{"jsonrpc":"2.0","id":123,"result":"OK"}"#);
/// ```
pub const fn ok(id: Id, result: T) -> Self {
Self {
jsonrpc: Version,
id,
payload: Ok(result),
}
}
/// Creates an error response.
///
/// ```rust
/// use cuprate_json_rpc::{Id, Response, error::{ErrorObject, ErrorCode}};
///
/// let err = ErrorObject {
/// code: 0.into(),
/// message: "m".into(),
/// data: Some("d".into()),
/// };
///
/// let ok = Response::<()>::err(Id::Num(123), err);
/// let json = serde_json::to_string(&ok).unwrap();
/// assert_eq!(json, r#"{"jsonrpc":"2.0","id":123,"error":{"code":0,"message":"m","data":"d"}}"#);
/// ```
pub const fn err(id: Id, error: ErrorObject) -> Self {
Self {
jsonrpc: Version,
id,
payload: Err(error),
}
}
/// Creates an error response using [`ErrorObject::parse_error`].
///
/// ```rust
/// use cuprate_json_rpc::{Id, Response, error::{ErrorObject, ErrorCode}};
///
/// let ok = Response::<()>::parse_error(Id::Num(0));
/// let json = serde_json::to_string(&ok).unwrap();
/// assert_eq!(json, r#"{"jsonrpc":"2.0","id":0,"error":{"code":-32700,"message":"Parse error"}}"#);
/// ```
pub const fn parse_error(id: Id) -> Self {
Self {
jsonrpc: Version,
payload: Err(ErrorObject::parse_error()),
id,
}
}
/// Creates an error response using [`ErrorObject::invalid_request`].
///
/// ```rust
/// use cuprate_json_rpc::{Id, Response, error::{ErrorObject, ErrorCode}};
///
/// let ok = Response::<()>::invalid_request(Id::Num(0));
/// let json = serde_json::to_string(&ok).unwrap();
/// assert_eq!(json, r#"{"jsonrpc":"2.0","id":0,"error":{"code":-32600,"message":"Invalid Request"}}"#);
/// ```
pub const fn invalid_request(id: Id) -> Self {
Self {
jsonrpc: Version,
payload: Err(ErrorObject::invalid_request()),
id,
}
}
/// Creates an error response using [`ErrorObject::method_not_found`].
///
/// ```rust
/// use cuprate_json_rpc::{Id, Response, error::{ErrorObject, ErrorCode}};
///
/// let ok = Response::<()>::method_not_found(Id::Num(0));
/// let json = serde_json::to_string(&ok).unwrap();
/// assert_eq!(json, r#"{"jsonrpc":"2.0","id":0,"error":{"code":-32601,"message":"Method not found"}}"#);
/// ```
pub const fn method_not_found(id: Id) -> Self {
Self {
jsonrpc: Version,
payload: Err(ErrorObject::method_not_found()),
id,
}
}
/// Creates an error response using [`ErrorObject::invalid_params`].
///
/// ```rust
/// use cuprate_json_rpc::{Id, Response, error::{ErrorObject, ErrorCode}};
///
/// let ok = Response::<()>::invalid_params(Id::Num(0));
/// let json = serde_json::to_string(&ok).unwrap();
/// assert_eq!(json, r#"{"jsonrpc":"2.0","id":0,"error":{"code":-32602,"message":"Invalid params"}}"#);
/// ```
pub const fn invalid_params(id: Id) -> Self {
Self {
jsonrpc: Version,
payload: Err(ErrorObject::invalid_params()),
id,
}
}
/// Creates an error response using [`ErrorObject::internal_error`].
///
/// ```rust
/// use cuprate_json_rpc::{Id, Response, error::{ErrorObject, ErrorCode}};
///
/// let ok = Response::<()>::internal_error(Id::Num(0));
/// let json = serde_json::to_string(&ok).unwrap();
/// assert_eq!(json, r#"{"jsonrpc":"2.0","id":0,"error":{"code":-32603,"message":"Internal error"}}"#);
/// ```
pub const fn internal_error(id: Id) -> Self {
Self {
jsonrpc: Version,
payload: Err(ErrorObject::internal_error()),
id,
}
}
}
//---------------------------------------------------------------------------------------------------- Trait impl
impl<T> std::fmt::Display for Response<T>
where
T: Serialize,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match serde_json::to_string_pretty(self) {
Ok(json) => write!(f, "{json}"),
Err(_) => Err(std::fmt::Error),
}
}
}
//---------------------------------------------------------------------------------------------------- Serde impl
impl<T> Serialize for Response<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct("Response", 3)?;
s.serialize_field("jsonrpc", &self.jsonrpc)?;
// This member is required.
//
// Even if `null`, or the client `Request` didn't include one.
s.serialize_field("id", &self.id)?;
match &self.payload {
Ok(r) => s.serialize_field("result", r)?,
Err(e) => s.serialize_field("error", e)?,
}
s.end()
}
}
// [`Response`] has a manual deserialization implementation because
// we need to confirm `result` and `error` don't both exist:
//
// > Either the result member or error member MUST be included, but both members MUST NOT be included.
//
// <https://www.jsonrpc.org/specification#error_object>
impl<'de, T> Deserialize<'de> for Response<T>
where
T: Deserialize<'de> + 'de,
{
fn deserialize<D: Deserializer<'de>>(der: D) -> Result<Self, D::Error> {
use std::marker::PhantomData;
use serde::de::{Error, MapAccess, Visitor};
/// This type represents the key values within [`Response`].
enum Key {
/// "jsonrpc" field.
JsonRpc,
/// "result" field.
Result,
/// "error" field.
Error,
/// "id" field.
Id,
/// Any other unknown field (ignored).
Unknown,
}
// Deserialization for [`Response`]'s key fields.
//
// This ignores unknown keys.
impl<'de> Deserialize<'de> for Key {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
/// Serde visitor for [`Response`]'s key fields.
struct KeyVisitor;
impl Visitor<'_> for KeyVisitor {
type Value = Key;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("`jsonrpc`, `id`, `result`, `error`")
}
fn visit_str<E>(self, string: &str) -> Result<Key, E>
where
E: Error,
{
// PERF: this match is in order of how this library serializes fields.
match string {
"jsonrpc" => Ok(Key::JsonRpc),
"id" => Ok(Key::Id),
"result" => Ok(Key::Result),
"error" => Ok(Key::Error),
// Ignore any other keys that appear
// and continue deserialization.
_ => Ok(Key::Unknown),
}
}
}
deserializer.deserialize_identifier(KeyVisitor)
}
}
/// Serde visitor for the key-value map of [`Response`].
struct MapVisit<T>(PhantomData<T>);
// Deserialization for [`Response`]'s key and values (the JSON map).
impl<'de, T> Visitor<'de> for MapVisit<T>
where
T: Deserialize<'de> + 'de,
{
type Value = Response<T>;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("JSON-RPC 2.0 Response")
}
/// This is a loop that goes over every key-value pair
/// and fills out the necessary fields.
///
/// If both `result/error` appear then this
/// deserialization will error, as to
/// follow the JSON-RPC 2.0 specification.
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
// Initialize values.
let mut jsonrpc = None;
let mut payload = None;
let mut id = None;
// Loop over map, filling values.
while let Some(key) = map.next_key::<Key>()? {
// PERF: this match is in order of how this library serializes fields.
match key {
Key::JsonRpc => jsonrpc = Some(map.next_value::<Version>()?),
Key::Id => id = Some(map.next_value::<Id>()?),
Key::Result => {
if payload.is_none() {
payload = Some(Ok(map.next_value::<T>()?));
} else {
return Err(Error::duplicate_field("result/error"));
}
}
Key::Error => {
if payload.is_none() {
payload = Some(Err(map.next_value::<ErrorObject>()?));
} else {
return Err(Error::duplicate_field("result/error"));
}
}
Key::Unknown => {
map.next_value::<serde::de::IgnoredAny>()?;
}
}
}
// Make sure all our key-value pairs are set and correct.
match (jsonrpc, id, payload) {
// Response with a single `result` or `error`.
(Some(jsonrpc), Some(id), Some(payload)) => Ok(Response {
jsonrpc,
id,
payload,
}),
// No fields existed.
(None, None, None) => Err(Error::missing_field("jsonrpc + id + result/error")),
// Some field was missing.
(None, _, _) => Err(Error::missing_field("jsonrpc")),
(_, None, _) => Err(Error::missing_field("id")),
(_, _, None) => Err(Error::missing_field("result/error")),
}
}
}
/// All expected fields of the [`Response`] type.
const FIELDS: &[&str; 4] = &["jsonrpc", "id", "result", "error"];
der.deserialize_struct("Response", FIELDS, MapVisit(PhantomData))
}
}
//---------------------------------------------------------------------------------------------------- TESTS
#[cfg(test)]
mod test {
use serde_json::json;
use super::*;
use crate::id::Id;
/// Basic serde test on OK results.
#[test]
fn serde_result() {
let result = String::from("result_ok");
let id = Id::Num(123);
let req = Response::ok(id.clone(), result.clone());
let ser: String = serde_json::to_string(&req).unwrap();
let de: Response<String> = serde_json::from_str(&ser).unwrap();
assert_eq!(de.payload.unwrap(), result);
assert_eq!(de.id, id);
}
/// Basic serde test on errors.
#[test]
fn serde_error() {
let error = ErrorObject::internal_error();
let id = Id::Num(123);
let req: Response<String> = Response::err(id.clone(), error.clone());
let ser: String = serde_json::to_string(&req).unwrap();
let de: Response<String> = serde_json::from_str(&ser).unwrap();
assert_eq!(de.payload.unwrap_err(), error);
assert_eq!(de.id, id);
}
/// Test that the `result` and `error` fields are mutually exclusive.
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: Error(\"duplicate field `result/error`\", line: 0, column: 0)"
)]
fn result_error_mutually_exclusive() {
let e = ErrorObject::internal_error();
let j = json!({
"jsonrpc": "2.0",
"id": 0,
"result": "",
"error": e
});
serde_json::from_value::<Response<String>>(j).unwrap();
}
/// Test that the `result` and `error` fields can repeat (and get overwritten).
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: Error(\"duplicate field `result/error`\", line: 1, column: 45)"
)]
fn result_repeat() {
// `result`
let json = r#"{"jsonrpc":"2.0","id":0,"result":"a","result":"b"}"#;
serde_json::from_str::<Response<String>>(json).unwrap();
}
/// Test that the `error` field cannot repeat.
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: Error(\"duplicate field `result/error`\", line: 1, column: 83)"
)]
fn error_repeat() {
let e = ErrorObject::invalid_request();
let e = serde_json::to_string(&e).unwrap();
let json = format!(r#"{{"jsonrpc":"2.0","id":0,"error":{e},"error":{e}}}"#);
serde_json::from_str::<Response<String>>(&json).unwrap();
}
/// Test that the `id` field must exist.
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: Error(\"missing field `id`\", line: 0, column: 0)"
)]
fn id_must_exist() {
let j = json!({
"jsonrpc": "2.0",
"result": "",
});
serde_json::from_value::<Response<String>>(j).unwrap();
}
/// Tests that non-ordered fields still deserialize okay.
#[test]
fn deserialize_out_of_order_keys() {
let e = ErrorObject::internal_error();
let j = json!({
"error": e,
"id": 0,
"jsonrpc": "2.0"
});
let resp = serde_json::from_value::<Response<String>>(j).unwrap();
assert_eq!(resp, Response::internal_error(Id::Num(0)));
let ok = Response::ok(Id::Num(0), "OK".to_string());
let j = json!({
"result": "OK",
"id": 0,
"jsonrpc": "2.0"
});
let resp = serde_json::from_value::<Response<String>>(j).unwrap();
assert_eq!(resp, ok);
}
/// Asserts that fields must be `lowercase`.
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: Error(\"missing field `jsonrpc`\", line: 1, column: 40)"
)]
fn lowercase() {
let mixed_case = r#"{"jSoNRPC":"2.0","id":123,"result":"OK"}"#;
serde_json::from_str::<Response<String>>(mixed_case).unwrap();
}
/// Tests that unknown fields are ignored, and deserialize continues.
/// Also that unicode and backslashes work.
#[test]
fn unknown_fields_and_unicode() {
let e = ErrorObject::internal_error();
let j = json!({
"error": e,
"\u{00f8}": 123,
"id": 0,
"unknown_field": 123,
"jsonrpc": "2.0",
"unknown_field": 123
});
let resp = serde_json::from_value::<Response<String>>(j).unwrap();
assert_eq!(resp, Response::internal_error(Id::Num(0)));
}
}