use std::fmt;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{from_value, Map, Value};
use crate::{error::Error, id::Id, v2::version::Version};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Params {
Array(Vec<Value>),
Map(Map<String, Value>),
}
impl Default for Params {
fn default() -> Self {
Params::Array(vec![])
}
}
impl fmt::Display for Params {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self).expect("`Params` is serializable");
write!(f, "{}", json)
}
}
impl Params {
pub fn parse<D>(self) -> Result<D, Error>
where
D: DeserializeOwned,
{
let value = self.into();
from_value(value).map_err(Error::invalid_params)
}
pub fn is_empty_array(&self) -> bool {
matches!(self, Params::Array(array) if array.is_empty())
}
pub fn is_array(&self) -> bool {
matches!(self, Params::Array(_))
}
pub fn is_map(&self) -> bool {
matches!(self, Params::Map(_))
}
}
impl From<Params> for Value {
fn from(params: Params) -> Value {
match params {
Params::Array(array) => Value::Array(array),
Params::Map(object) => Value::Object(object),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MethodCall {
pub jsonrpc: Version,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Params>,
pub id: Id,
}
impl fmt::Display for MethodCall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self).expect("`MethodCall` is serializable");
write!(f, "{}", json)
}
}
impl MethodCall {
pub fn new<M: Into<String>>(method: M, params: Option<Params>, id: Id) -> Self {
Self {
jsonrpc: Version::V2_0,
method: method.into(),
params,
id,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Notification {
pub jsonrpc: Version,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Params>,
}
impl fmt::Display for Notification {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self).expect("`Notification` is serializable");
write!(f, "{}", json)
}
}
impl Notification {
pub fn new<M: Into<String>>(method: M, params: Option<Params>) -> Self {
Self {
jsonrpc: Version::V2_0,
method: method.into(),
params,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SubscriptionNotificationParams<T = Value> {
pub subscription: Id,
pub result: T,
}
impl<T: Serialize + DeserializeOwned> SubscriptionNotificationParams<T> {
pub fn new(id: Id, result: T) -> Self {
Self {
subscription: id,
result,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SubscriptionNotification<T = Value> {
pub jsonrpc: Version,
pub method: String,
pub params: SubscriptionNotificationParams<T>,
}
impl<T: Serialize> fmt::Display for SubscriptionNotification<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self).expect("`SubscriptionNotification` is serializable");
write!(f, "{}", json)
}
}
impl<T: Serialize + DeserializeOwned> SubscriptionNotification<T> {
pub fn new<M: Into<String>>(method: M, params: SubscriptionNotificationParams<T>) -> Self {
Self {
jsonrpc: Version::V2_0,
method: method.into(),
params,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum Call {
MethodCall(MethodCall),
Notification(Notification),
}
impl fmt::Display for Call {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self).expect("`Call` is serializable");
write!(f, "{}", json)
}
}
impl Call {
pub fn method(&self) -> &str {
match self {
Self::MethodCall(call) => &call.method,
Self::Notification(notification) => ¬ification.method,
}
}
pub fn params(&self) -> &Option<Params> {
match self {
Self::MethodCall(call) => &call.params,
Self::Notification(notification) => ¬ification.params,
}
}
pub fn id(&self) -> Option<Id> {
match self {
Self::MethodCall(call) => Some(call.id.clone()),
Self::Notification(_notification) => None,
}
}
}
impl From<MethodCall> for Call {
fn from(call: MethodCall) -> Self {
Self::MethodCall(call)
}
}
impl From<Notification> for Call {
fn from(notify: Notification) -> Self {
Self::Notification(notify)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum Request {
Single(Call),
Batch(Vec<Call>),
}
impl fmt::Display for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self).expect("`Request` is serializable");
write!(f, "{}", json)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum MethodCallRequest {
Single(MethodCall),
Batch(Vec<MethodCall>),
}
impl From<MethodCall> for MethodCallRequest {
fn from(call: MethodCall) -> Self {
Self::Single(call)
}
}
impl From<Vec<MethodCall>> for MethodCallRequest {
fn from(calls: Vec<MethodCall>) -> Self {
Self::Batch(calls)
}
}
impl fmt::Display for MethodCallRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self).expect("`MethodCallRequest` is serializable");
write!(f, "{}", json)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn params_serialization() {
let array = vec![Value::from(1), Value::Bool(true)];
let params = Params::Array(array.clone());
assert_eq!(serde_json::to_string(¶ms).unwrap(), r#"[1,true]"#);
assert_eq!(
serde_json::from_str::<Params>(r#"[1,true]"#).unwrap(),
params
);
let object = {
let mut map = Map::new();
map.insert("key".into(), Value::String("value".into()));
map
};
let params = Params::Map(object.clone());
assert_eq!(
serde_json::to_string(¶ms).unwrap(),
r#"{"key":"value"}"#
);
assert_eq!(
serde_json::from_str::<Params>(r#"{"key":"value"}"#).unwrap(),
params
);
let params = Params::Array(vec![
Value::Null,
Value::Bool(true),
Value::from(-1),
Value::from(1),
Value::from(1.2),
Value::String("hello".to_string()),
Value::Array(vec![]),
Value::Array(array),
Value::Object(object),
]);
assert_eq!(
serde_json::to_string(¶ms).unwrap(),
r#"[null,true,-1,1,1.2,"hello",[],[1,true],{"key":"value"}]"#
);
assert_eq!(
serde_json::from_str::<Params>(
r#"[null,true,-1,1,1.2,"hello",[],[1,true],{"key":"value"}]"#
)
.unwrap(),
params
);
}
#[test]
fn single_param_parsed_as_tuple() {
let params: (u64,) = Params::Array(vec![Value::from(1)]).parse().unwrap();
assert_eq!(params, (1,));
}
#[test]
fn invalid_params() {
let params = serde_json::from_str::<Params>("[1,true]").unwrap();
assert_eq!(
params.parse::<(u8, bool, String)>().unwrap_err(),
Error::invalid_params("invalid length 2, expected a tuple of size 3")
);
}
fn method_call_cases() -> Vec<(MethodCall, &'static str)> {
vec![
(
MethodCall {
jsonrpc: Version::V2_0,
method: "foo".to_string(),
params: Some(Params::Array(vec![Value::from(1), Value::Bool(true)])),
id: Id::Num(1),
},
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true],"id":1}"#,
),
(
MethodCall {
jsonrpc: Version::V2_0,
method: "foo".to_string(),
params: Some(Params::Array(vec![])),
id: Id::Num(1),
},
r#"{"jsonrpc":"2.0","method":"foo","params":[],"id":1}"#,
),
(
MethodCall {
jsonrpc: Version::V2_0,
method: "foo".to_string(),
params: None,
id: Id::Num(1),
},
r#"{"jsonrpc":"2.0","method":"foo","id":1}"#,
),
]
}
fn notification_cases() -> Vec<(Notification, &'static str)> {
vec![
(
Notification {
jsonrpc: Version::V2_0,
method: "foo".to_string(),
params: Some(Params::Array(vec![Value::from(1), Value::Bool(true)])),
},
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true]}"#,
),
(
Notification {
jsonrpc: Version::V2_0,
method: "foo".to_string(),
params: Some(Params::Array(vec![])),
},
r#"{"jsonrpc":"2.0","method":"foo","params":[]}"#,
),
(
Notification {
jsonrpc: Version::V2_0,
method: "foo".to_string(),
params: None,
},
r#"{"jsonrpc":"2.0","method":"foo"}"#,
),
]
}
#[test]
fn method_call_serialization() {
for (method_call, expect) in method_call_cases() {
let ser = serde_json::to_string(&method_call).unwrap();
assert_eq!(ser, expect);
let de = serde_json::from_str::<MethodCall>(expect).unwrap();
assert_eq!(de, method_call);
}
}
#[test]
fn notification_serialization() {
for (notification, expect) in notification_cases() {
let ser = serde_json::to_string(¬ification).unwrap();
assert_eq!(ser, expect);
let de = serde_json::from_str::<Notification>(expect).unwrap();
assert_eq!(de, notification);
}
}
#[test]
fn call_serialization() {
for (method_call, expect) in method_call_cases() {
let call = Call::MethodCall(method_call);
assert_eq!(serde_json::to_string(&call).unwrap(), expect);
assert_eq!(serde_json::from_str::<Call>(expect).unwrap(), call);
}
for (notification, expect) in notification_cases() {
let call = Call::Notification(notification);
assert_eq!(serde_json::to_string(&call).unwrap(), expect);
assert_eq!(serde_json::from_str::<Call>(expect).unwrap(), call);
}
}
#[test]
fn request_serialization() {
for (method_call, expect) in method_call_cases() {
let call_request = Request::Single(Call::MethodCall(method_call));
assert_eq!(serde_json::to_string(&call_request).unwrap(), expect);
assert_eq!(
serde_json::from_str::<Request>(expect).unwrap(),
call_request
);
}
for (notification, expect) in notification_cases() {
let notification_request = Request::Single(Call::Notification(notification));
assert_eq!(
serde_json::to_string(¬ification_request).unwrap(),
expect
);
assert_eq!(
serde_json::from_str::<Request>(expect).unwrap(),
notification_request
);
}
let batch_request = Request::Batch(vec![
Call::MethodCall(MethodCall::new("foo", None, 1.into())),
Call::MethodCall(MethodCall::new("bar", None, 2.into())),
]);
let batch_expect =
r#"[{"jsonrpc":"2.0","method":"foo","id":1},{"jsonrpc":"2.0","method":"bar","id":2}]"#;
assert_eq!(serde_json::to_string(&batch_request).unwrap(), batch_expect);
assert_eq!(
serde_json::from_str::<Request>(&batch_expect).unwrap(),
batch_request
);
}
#[test]
fn invalid_request() {
let cases = vec![
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true],"id":1,"unknown":[]}"#,
r#"{"jsonrpc":"2.0"`,"method":"foo","params":[1,true],"id":1.2}"#,
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true],"id":null,"unknown":[]}"#,
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true],"id":null}"#,
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true],"unknown":[]}"#,
r#"{"jsonrpc":"2.0","method":"foo","unknown":[]}"#,
r#"{"jsonrpc":"2.0","unknown":[]}"#,
];
for case in cases {
let request = serde_json::from_str::<Request>(case);
assert!(request.is_err());
}
}
#[test]
fn valid_request() {
let cases = vec![
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true],"id":1}"#,
r#"{"jsonrpc":"2.0","method":"foo","params":[],"id":1}"#,
r#"{"jsonrpc":"2.0","method":"foo","id":1}"#,
r#"{"jsonrpc":"2.0","method":"foo","params":[1,true]}"#,
r#"{"jsonrpc":"2.0","method":"foo","params":[]}"#,
r#"{"jsonrpc":"2.0","method":"foo"}"#,
];
for case in cases {
let request = serde_json::from_str::<Request>(case);
assert!(request.is_ok());
}
}
}