use crate::support::get_json_type;
use crate::{RpcId, RpcRequestParsingError};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serializer};
use serde_json::Value;
#[derive(Deserialize, Clone, Debug)]
pub struct RpcRequest {
pub id: RpcId,
pub method: String,
pub params: Option<Value>,
}
impl RpcRequest {
pub fn new(id: impl Into<RpcId>, method: impl Into<String>, params: Option<Value>) -> Self {
RpcRequest {
id: id.into(),
method: method.into(),
params,
}
}
}
impl RpcRequest {
pub fn from_value(value: Value) -> Result<RpcRequest, RpcRequestParsingError> {
RpcRequest::from_value_with_checks(value, RpcRequestCheckFlags::ALL)
}
pub fn from_value_with_checks(
value: Value,
checks: RpcRequestCheckFlags,
) -> Result<RpcRequest, RpcRequestParsingError> {
let value_type = get_json_type(&value);
let Value::Object(mut obj) = value else {
return Err(RpcRequestParsingError::RequestInvalidType {
actual_type: value_type.to_string(),
});
};
if checks.contains(RpcRequestCheckFlags::VERSION) {
match obj.remove("jsonrpc") {
Some(version) => {
if version.as_str().unwrap_or_default() != "2.0" {
let (id_val, method) = extract_id_value_and_method(obj);
return Err(RpcRequestParsingError::VersionInvalid {
id: id_val,
method,
version,
});
}
}
None => {
let (id_val, method) = extract_id_value_and_method(obj);
return Err(RpcRequestParsingError::VersionMissing { id: id_val, method });
}
}
}
let rpc_id_value: Option<Value> = obj.remove("id");
let method = match obj.remove("method") {
None => {
return Err(RpcRequestParsingError::MethodMissing { id: rpc_id_value });
}
Some(method_val) => match method_val {
Value::String(method_name) => method_name,
other => {
return Err(RpcRequestParsingError::MethodInvalidType {
id: rpc_id_value,
method: other,
});
}
},
};
let check_id = checks.contains(RpcRequestCheckFlags::ID);
let id = match rpc_id_value {
None => {
if check_id {
return Err(RpcRequestParsingError::IdMissing { method: Some(method) });
} else {
RpcId::Null
}
}
Some(id_value) => match RpcId::from_value(id_value) {
Ok(rpc_id) => rpc_id,
Err(err) => {
if check_id {
return Err(err);
} else {
RpcId::Null
}
}
},
};
let params = obj.get_mut("params").map(Value::take);
Ok(RpcRequest { id, method, params })
}
}
impl serde::Serialize for RpcRequest {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut field_count = 3;
if self.params.is_some() {
field_count += 1;
}
let mut state = serializer.serialize_struct("RpcRequest", field_count)?;
state.serialize_field("jsonrpc", "2.0")?;
state.serialize_field("id", &self.id)?;
state.serialize_field("method", &self.method)?;
if let Some(params) = &self.params {
state.serialize_field("params", params)?;
}
state.end()
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RpcRequestCheckFlags: u32 {
const VERSION = 0b00000001;
const ID = 0b00000010;
const ALL = Self::VERSION.bits() | Self::ID.bits();
}
}
fn extract_id_value_and_method(mut obj: serde_json::Map<String, Value>) -> (Option<Value>, Option<String>) {
let id = obj.remove("id");
let method = obj.remove("method").and_then(|v| v.as_str().map(|s| s.to_string()));
(id, method)
}
impl TryFrom<Value> for RpcRequest {
type Error = RpcRequestParsingError;
fn try_from(value: Value) -> Result<RpcRequest, RpcRequestParsingError> {
RpcRequest::from_value(value)
}
}