Skip to content

Commit

Permalink
types: add hashmap for non-standard response fields
Browse files Browse the repository at this point in the history
  • Loading branch information
thebabush committed Apr 25, 2024
1 parent 8b8811b commit 84130ec
Showing 1 changed file with 51 additions and 15 deletions.
66 changes: 51 additions & 15 deletions types/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
//! Types pertaining to JSON-RPC responses.

use std::borrow::Cow as StdCow;
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;

Expand All @@ -45,17 +46,24 @@ pub struct Response<'a, T: Clone> {
pub payload: ResponsePayload<'a, T>,
/// Request ID
pub id: Id<'a>,
/// Non-standard fields
pub other: HashMap<String, Box<serde_json::value::RawValue>>,
}

impl<'a, T: Clone> Response<'a, T> {
/// Create a new [`Response`].
pub fn new(payload: ResponsePayload<'a, T>, id: Id<'a>) -> Response<'a, T> {
Response { jsonrpc: Some(TwoPointZero), payload, id }
Response { jsonrpc: Some(TwoPointZero), payload, id, other: HashMap::new() }
}

/// Create an owned [`Response`].
pub fn into_owned(self) -> Response<'static, T> {
Response { jsonrpc: self.jsonrpc, payload: self.payload.into_owned(), id: self.id.into_owned() }
Response {
jsonrpc: self.jsonrpc,
payload: self.payload.into_owned(),
id: self.id.into_owned(),
other: self.other,
}
}
}

Expand Down Expand Up @@ -195,7 +203,7 @@ where
Result,
Error,
Id,
Ignore,
Other(String),
}

impl<'de> Deserialize<'de> for Field {
Expand All @@ -221,7 +229,7 @@ where
"result" => Ok(Field::Result),
"error" => Ok(Field::Error),
"id" => Ok(Field::Id),
_ => Ok(Field::Ignore),
_ => Ok(Field::Other(value.to_owned())),
}
}
}
Expand Down Expand Up @@ -255,6 +263,7 @@ where
let mut result = None;
let mut error = None;
let mut id = None;
let mut other: HashMap<String, Box<serde_json::value::RawValue>> = HashMap::new();
while let Some(key) = map.next_key()? {
match key {
Field::Result => {
Expand All @@ -281,8 +290,10 @@ where
}
jsonrpc = Some(map.next_value()?);
}
Field::Ignore => {
let _ = map.next_value::<serde::de::IgnoredAny>()?;
Field::Other(key) => {
if other.insert(key.to_owned(), map.next_value()?).is_some() {
return Err(serde::de::Error::custom(format!("duplicated field \"{key}\"")));
}
}
}
}
Expand All @@ -294,13 +305,15 @@ where
return Err(serde::de::Error::duplicate_field("result and error are mutually exclusive"))
}
(Some(jsonrpc), Some(result), None) => {
Response { jsonrpc, payload: ResponsePayload::Success(result), id }
Response { jsonrpc, payload: ResponsePayload::Success(result), id, other }
}
(Some(jsonrpc), None, Some(err)) => {
Response { jsonrpc, payload: ResponsePayload::Error(err), id, other }
}
(Some(jsonrpc), None, Some(err)) => Response { jsonrpc, payload: ResponsePayload::Error(err), id },
(None, Some(result), _) => {
Response { jsonrpc: None, payload: ResponsePayload::Success(result), id }
Response { jsonrpc: None, payload: ResponsePayload::Success(result), id, other }
}
(None, _, Some(err)) => Response { jsonrpc: None, payload: ResponsePayload::Error(err), id },
(None, _, Some(err)) => Response { jsonrpc: None, payload: ResponsePayload::Error(err), id, other },
(_, None, None) => return Err(serde::de::Error::missing_field("result/error")),
};

Expand Down Expand Up @@ -339,6 +352,8 @@ where

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use super::{Id, Response, TwoPointZero};
use crate::{response::ResponsePayload, ErrorObjectOwned};

Expand All @@ -348,6 +363,7 @@ mod tests {
jsonrpc: Some(TwoPointZero),
payload: ResponsePayload::success("ok"),
id: Id::Number(1),
other: HashMap::new(),
})
.unwrap();
let exp = r#"{"jsonrpc":"2.0","result":"ok","id":1}"#;
Expand All @@ -360,6 +376,7 @@ mod tests {
jsonrpc: Some(TwoPointZero),
payload: ResponsePayload::<()>::error(ErrorObjectOwned::owned(1, "lo", None::<()>)),
id: Id::Number(1),
other: HashMap::new(),
})
.unwrap();
let exp = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"lo"},"id":1}"#;
Expand All @@ -372,6 +389,7 @@ mod tests {
jsonrpc: None,
payload: ResponsePayload::success("ok"),
id: Id::Number(1),
other: HashMap::new(),
})
.unwrap();
let exp = r#"{"result":"ok","id":1}"#;
Expand All @@ -380,8 +398,12 @@ mod tests {

#[test]
fn deserialize_success_call() {
let exp =
Response { jsonrpc: Some(TwoPointZero), payload: ResponsePayload::success(99_u64), id: Id::Number(11) };
let exp = Response {
jsonrpc: Some(TwoPointZero),
payload: ResponsePayload::success(99_u64),
id: Id::Number(11),
other: HashMap::new(),
};
let dsr: Response<u64> = serde_json::from_str(r#"{"jsonrpc":"2.0", "result":99, "id":11}"#).unwrap();
assert_eq!(dsr.jsonrpc, exp.jsonrpc);
assert_eq!(dsr.payload, exp.payload);
Expand All @@ -394,6 +416,7 @@ mod tests {
jsonrpc: Some(TwoPointZero),
payload: ResponsePayload::error(ErrorObjectOwned::owned(1, "lo", None::<()>)),
id: Id::Number(11),
other: HashMap::new(),
};
let dsr: Response<()> =
serde_json::from_str(r#"{"jsonrpc":"2.0","error":{"code":1,"message":"lo"},"id":11}"#).unwrap();
Expand All @@ -404,20 +427,33 @@ mod tests {

#[test]
fn deserialize_call_missing_version_field() {
let exp = Response { jsonrpc: None, payload: ResponsePayload::success(99_u64), id: Id::Number(11) };
let exp = Response {
jsonrpc: None,
payload: ResponsePayload::success(99_u64),
id: Id::Number(11),
other: HashMap::new(),
};
let dsr: Response<u64> = serde_json::from_str(r#"{"jsonrpc":null, "result":99, "id":11}"#).unwrap();
assert_eq!(dsr.jsonrpc, exp.jsonrpc);
assert_eq!(dsr.payload, exp.payload);
assert_eq!(dsr.id, exp.id);
}

#[test]
fn deserialize_with_unknown_field() {
let exp = Response { jsonrpc: None, payload: ResponsePayload::success(99_u64), id: Id::Number(11) };
fn deserialize_with_other_field() {
let exp = Response {
jsonrpc: None,
payload: ResponsePayload::success(99_u64),
id: Id::Number(11),
other: HashMap::new(),
};
let dsr: Response<u64> =
serde_json::from_str(r#"{"jsonrpc":null, "result":99, "id":11, "unknown":11}"#).unwrap();
assert_eq!(dsr.jsonrpc, exp.jsonrpc);
assert_eq!(dsr.payload, exp.payload);
assert_eq!(dsr.id, exp.id);
let (k, v) = dsr.other.iter().next().unwrap();
assert_eq!(k, "unknown");
assert_eq!(v.get(), "11");
}
}

0 comments on commit 84130ec

Please sign in to comment.