Skip to content

Commit

Permalink
Add support for String and Null Ids
Browse files Browse the repository at this point in the history
  • Loading branch information
ebin-mathews authored and 0xdeafbeef committed Mar 25, 2024
1 parent 531e2b3 commit 88d0d63
Showing 1 changed file with 53 additions and 24 deletions.
77 changes: 53 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub type JrpcResult = Result<JsonRpcResponse, JsonRpcResponse>;

#[derive(Debug)]
pub struct JsonRpcRequest {
pub id: i64,
pub id: Id,
pub method: String,
pub params: Value,
}
Expand All @@ -80,14 +80,14 @@ impl Serialize for JsonRpcRequest {
#[derive(Serialize)]
struct Helper<'a> {
jsonrpc: &'static str,
id: i64,
id: Id,
method: &'a str,
params: &'a Value,
}

Helper {
jsonrpc: JSONRPC,
id: self.id,
id: self.id.clone(),
method: &self.method,
params: &self.params,
}
Expand All @@ -106,7 +106,7 @@ impl<'de> Deserialize<'de> for JsonRpcRequest {
struct Helper<'a> {
#[serde(borrow)]
jsonrpc: Cow<'a, str>,
id: i64,
id: Id,
method: String,
params: Value,
}
Expand Down Expand Up @@ -145,12 +145,12 @@ impl<'de> Deserialize<'de> for JsonRpcRequest {
pub struct JsonRpcExtractor {
pub parsed: Value,
pub method: String,
pub id: i64,
pub id: Id,
}

impl JsonRpcExtractor {
pub fn get_answer_id(&self) -> i64 {
self.id
pub fn get_answer_id(&self) -> Id {
self.id.clone()
}

pub fn parse_params<T: DeserializeOwned>(self) -> Result<T, JsonRpcResponse> {
Expand Down Expand Up @@ -195,7 +195,7 @@ impl JsonRpcExtractor {
Value::default(),
);

JsonRpcResponse::error(self.id, error)
JsonRpcResponse::error(self.id.clone(), error)
}
}

Expand All @@ -210,7 +210,7 @@ where
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
if !json_content_type(req.headers()) {
return Err(JsonRpcResponse {
id: 0,
id: Id::Num(0),
result: JsonRpcAnswer::Error(JsonRpcError::new(
JsonRpcErrorReason::InvalidRequest,
"Invalid content type".to_owned(),
Expand All @@ -224,7 +224,7 @@ where
Ok(a) => a.to_vec(),
Err(_) => {
return Err(JsonRpcResponse {
id: 0,
id: Id::Num(0),
result: JsonRpcAnswer::Error(JsonRpcError::new(
JsonRpcErrorReason::InvalidRequest,
"Invalid request".to_owned(),
Expand All @@ -240,7 +240,7 @@ where
Ok(a) => a,
Err(e) => {
return Err(JsonRpcResponse {
id: 0,
id: Id::Num(0),
result: JsonRpcAnswer::Error(JsonRpcError::new(
JsonRpcErrorReason::InvalidRequest,
e.to_string(),
Expand All @@ -254,7 +254,7 @@ where
Ok(a) => a,
Err(e) => {
return Err(JsonRpcResponse {
id: 0,
id: Id::Num(0),
result: JsonRpcAnswer::Error(JsonRpcError::new(
JsonRpcErrorReason::InvalidRequest,
e.to_string(),
Expand Down Expand Up @@ -305,17 +305,17 @@ pub struct JsonRpcResponse {
/// Request content.
pub result: JsonRpcAnswer,
/// The request ID.
pub id: i64,
pub id: Id,
}

impl JsonRpcResponse {
fn new(id: i64, result: JsonRpcAnswer) -> Self {
fn new(id: Id, result: JsonRpcAnswer) -> Self {
Self { result, id }
}

/// Returns a response with the given result
/// Returns JsonRpcError if the `result` is invalid input for [`serde_json::to_value`]
pub fn success<T: Serialize>(id: i64, result: T) -> Self {
pub fn success<T: Serialize>(id: Id, result: T) -> Self {
cfg_if::cfg_if! {
if #[cfg(feature = "simd")] {
match simd_json::serde::to_owned_value(result) {
Expand Down Expand Up @@ -345,7 +345,7 @@ impl JsonRpcResponse {
}
}

pub fn error(id: i64, error: JsonRpcError) -> Self {
pub fn error(id: Id, error: JsonRpcError) -> Self {
JsonRpcResponse {
result: JsonRpcAnswer::Error(error),
id,
Expand All @@ -363,13 +363,13 @@ impl Serialize for JsonRpcResponse {
jsonrpc: &'static str,
#[serde(flatten)]
result: &'a JsonRpcAnswer,
id: i64,
id: Id,
}

Helper {
jsonrpc: JSONRPC,
result: &self.result,
id: self.id,
id: self.id.clone(),
}
.serialize(serializer)
}
Expand All @@ -388,7 +388,7 @@ impl<'de> Deserialize<'de> for JsonRpcResponse {
jsonrpc: Cow<'a, str>,
#[serde(flatten)]
result: JsonRpcAnswer,
id: i64,
id: Id,
}

let helper = Helper::deserialize(deserializer)?;
Expand Down Expand Up @@ -419,12 +419,41 @@ pub enum JsonRpcAnswer {

const JSONRPC: &str = "2.0";

/// 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 to be a notification.
/// The value SHOULD normally not be Null and Numbers SHOULD NOT contain fractional parts
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash)]
#[serde(untagged)]
pub enum Id {
Num(i64),
Str(String),
None(()),
}

impl From<()> for Id {
fn from(val: ()) -> Self {
Id::None(val)
}
}

impl From<i64> for Id {
fn from(val: i64) -> Self {
Id::Num(val)
}
}

impl From<String> for Id {
fn from(val: String) -> Self {
Id::Str(val)
}
}

#[cfg(test)]
#[cfg(all(feature = "anyhow_error", feature = "serde_json"))]
mod test {
use crate::{
Deserialize, JrpcResult, JsonRpcAnswer, JsonRpcError, JsonRpcErrorReason, JsonRpcExtractor,
JsonRpcRequest, JsonRpcResponse,
Deserialize, Id, JrpcResult, JsonRpcAnswer, JsonRpcError, JsonRpcErrorReason,
JsonRpcExtractor, JsonRpcRequest, JsonRpcResponse,
};
use axum::routing::post;
use serde::Serialize;
Expand All @@ -445,7 +474,7 @@ mod test {
let res = client
.post("/")
.json(&JsonRpcRequest {
id: 0,
id: Id::Num(0),
method: "add".to_owned(),
params: serde_json::to_value(Test { a: 0, b: 111 }).unwrap(),
})
Expand All @@ -457,7 +486,7 @@ mod test {
let res = client
.post("/")
.json(&JsonRpcRequest {
id: 0,
id: Id::Num(0),
method: "lol".to_owned(),
params: serde_json::to_value(()).unwrap(),
})
Expand All @@ -473,7 +502,7 @@ mod test {
Value::Null,
);

let error = JsonRpcResponse::error(0, error);
let error = JsonRpcResponse::error(Id::Num(0), error);

assert_eq!(
serde_json::to_value(error).unwrap(),
Expand Down

0 comments on commit 88d0d63

Please sign in to comment.