diff --git a/core/src/types/params.rs b/core/src/types/params.rs index 2eac3860..cdf0f8c4 100644 --- a/core/src/types/params.rs +++ b/core/src/types/params.rs @@ -36,6 +36,11 @@ impl Params { p => Err(Error::invalid_params_with_details("No parameters were expected", p)), } } + + /// Check for None + pub fn is_none(&self) -> bool { + matches!(*self, Params::None) + } } impl From for Value { @@ -110,4 +115,12 @@ mod tests { let params: (u64,) = Params::Array(vec![Value::from(1)]).parse().unwrap(); assert_eq!(params, (1,)); } + + #[test] + fn detect_none() { + let none = Params::None; + assert!(none.is_none()); + let some = Params::Array(vec![]); + assert!(!some.is_none()); + } } diff --git a/core/src/types/request.rs b/core/src/types/request.rs index adbf0ecc..d5371498 100644 --- a/core/src/types/request.rs +++ b/core/src/types/request.rs @@ -12,7 +12,7 @@ pub struct MethodCall { pub method: String, /// A Structured value that holds the parameter values to be used /// during the invocation of the method. This member MAY be omitted. - #[serde(default = "default_params")] + #[serde(default = "default_params", skip_serializing_if = "Params::is_none")] pub params: Params, /// An identifier established by the Client that MUST contain a String, /// Number, or NULL value if included. If it is not included it is assumed @@ -105,6 +105,21 @@ mod tests { ); } + #[test] + fn call_serialize_without_params() { + use serde_json; + + let m = MethodCall { + jsonrpc: Some(Version::V2), + method: "status".to_owned(), + params: Params::None, + id: Id::Num(1), + }; + + let serialized = serde_json::to_string(&m).unwrap(); + assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"status","id":1}"#); + } + #[test] fn notification_serialize() { use serde_json; diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index 1f448367..4ab1d505 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -30,6 +30,10 @@ pub trait Rpc { #[rpc(name = "raw", params = "raw")] fn raw(&self, params: Params) -> Result; + /// Squares a number, default value is 1. + #[rpc(name = "sqr")] + fn sqr(&self, a: Option) -> Result; + /// Handles a notification. #[rpc(name = "notify")] fn notify(&self, a: u64); @@ -55,6 +59,11 @@ impl Rpc for RpcImpl { Ok("OK".into()) } + fn sqr(&self, a: Option) -> Result { + let a = a.unwrap_or(1); + Ok(a * a) + } + fn notify(&self, a: u64) { println!("Received `notify` with value: {}", a); } @@ -222,6 +231,49 @@ fn should_accept_any_raw_params() { assert_eq!(expected, result4); } +#[test] +fn should_accept_optional_param() { + let mut io = IoHandler::new(); + let rpc = RpcImpl::default(); + io.extend_with(rpc.to_delegate()); + + // when + let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"sqr","params":[2]}"#; + let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"sqr","params":[]}"#; + let req3 = r#"{"jsonrpc":"2.0","id":1,"method":"sqr","params":null}"#; + let req4 = r#"{"jsonrpc":"2.0","id":1,"method":"sqr"}"#; + + let res1 = io.handle_request_sync(req1); + let res2 = io.handle_request_sync(req2); + let res3 = io.handle_request_sync(req3); + let res4 = io.handle_request_sync(req4); + let expected1 = r#"{ + "jsonrpc": "2.0", + "result": 4, + "id": 1 + }"#; + let expected1: Response = serde_json::from_str(expected1).unwrap(); + let expected2 = r#"{ + "jsonrpc": "2.0", + "result": 1, + "id": 1 + }"#; + let expected2: Response = serde_json::from_str(expected2).unwrap(); + + // then + let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap(); + assert_eq!(expected1, result1); + + let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap(); + assert_eq!(expected2, result2); + + let result3: Response = serde_json::from_str(&res3.unwrap()).unwrap(); + assert_eq!(expected2, result3); + + let result4: Response = serde_json::from_str(&res4.unwrap()).unwrap(); + assert_eq!(expected2, result4); +} + #[test] fn should_accept_only_notifications() { let mut io = IoHandler::new();