use std::sync::Arc;
use derive_more::{Display, From};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use super::Error;
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(
Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize, PartialOrd, Ord, Display, From,
)]
#[serde(untagged)]
#[allow(
clippy::exhaustive_enums,
reason = "Request IDs are limited to JSON-RPC's non-null ID types"
)]
#[from(String, i64)]
pub enum RequestId {
Number(i64),
Str(String),
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[allow(
clippy::exhaustive_structs,
reason = "This comes from the JSON-RPC specification itself"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "{Params}", extend("x-docs-ignore" = true)))]
#[skip_serializing_none]
pub struct Request<Params> {
pub id: RequestId,
pub method: Arc<str>,
pub params: Option<Params>,
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[allow(
clippy::exhaustive_enums,
reason = "This comes from the JSON-RPC specification itself"
)]
#[serde(untagged)]
#[cfg_attr(feature = "schemars", schemars(rename = "{Result}", extend("x-docs-ignore" = true)))]
pub enum Response<Result> {
Result {
id: RequestId,
#[serde(
deserialize_with = "Deserialize::deserialize",
bound(deserialize = "Result: Deserialize<'de>")
)]
result: Result,
},
Error {
#[serde(deserialize_with = "Option::deserialize")]
#[cfg_attr(feature = "schemars", schemars(required))]
id: Option<RequestId>,
error: Error,
},
}
impl<R> Response<R> {
#[must_use]
pub fn new(id: impl Into<RequestId>, result: std::result::Result<R, Error>) -> Self {
match result {
Ok(result) => Self::Result {
id: id.into(),
result,
},
Err(error) => Self::Error {
id: Some(id.into()),
error,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{Value, json};
#[test]
fn request_id_rejects_null() {
assert!(serde_json::from_value::<RequestId>(Value::Null).is_err());
assert!(
serde_json::from_value::<Request<Value>>(json!({ "id": null, "method": "m" })).is_err()
);
assert_eq!(
serde_json::from_value::<RequestId>(json!(-1)).unwrap(),
RequestId::Number(-1)
);
assert_eq!(
serde_json::from_value::<RequestId>(json!("id")).unwrap(),
RequestId::Str("id".to_owned())
);
}
#[test]
fn error_response_id_is_required_and_nullable() {
let error = json!({ "code": -32700, "message": "Parse error" });
let response =
serde_json::from_value::<Response<Value>>(json!({ "id": null, "error": error }))
.unwrap();
assert!(matches!(response, Response::Error { id: None, .. }));
assert_eq!(
serde_json::to_value(&response).unwrap(),
json!({ "id": null, "error": error })
);
assert!(serde_json::from_value::<Response<Value>>(json!({ "error": error })).is_err());
assert!(
serde_json::from_value::<Response<Value>>(json!({ "id": null, "result": {} })).is_err()
);
}
}