mod.rs 3.66 KiB
Newer Older
Maciej Hirsz's avatar
Maciej Hirsz committed
use beef::lean::Cow;
use serde::de::{self, Deserializer, Unexpected, Visitor};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use std::fmt;
/// Error type.
pub mod error;
/// Traits.
pub mod traits;
pub use error::RpcError;
/// [JSON-RPC request object](https://www.jsonrpc.org/specification#request-object)
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct JsonRpcRequest<'a> {
	/// JSON-RPC version.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub jsonrpc: TwoPointZero,
	/// Request ID
Maciej Hirsz's avatar
Maciej Hirsz committed
	#[serde(borrow)]
	pub id: Option<&'a RawValue>,
	/// Name of the method to be invoked.
Maciej Hirsz's avatar
Maciej Hirsz committed
	#[serde(borrow)]
	pub method: Cow<'a, str>,
	/// Parameter values of the request.
Maciej Hirsz's avatar
Maciej Hirsz committed
	#[serde(borrow)]
	pub params: Option<&'a RawValue>,
}

/// Invalid request with known request ID.
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Deserialize, Debug)]
pub struct JsonRpcInvalidRequest<'a> {
	/// Request ID
Maciej Hirsz's avatar
Maciej Hirsz committed
	#[serde(borrow)]
	pub id: Option<&'a RawValue>,
}

/// JSON-RPC notification (a request object without a request ID).
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Serialize, Debug)]
pub struct JsonRpcNotification<'a> {
	/// JSON-RPC version.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub jsonrpc: TwoPointZero,
	/// Name of the method to be invoked.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub method: &'a str,
	/// Parameter values of the request.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub params: JsonRpcNotificationParams<'a>,
}

/// JSON-RPC parameter values for subscriptions.
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Serialize, Debug)]
pub struct JsonRpcNotificationParams<'a> {
	/// Subscription ID
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub subscription: u64,
	/// Result.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub result: &'a RawValue,
}

/// JSON-RPC successful response object.
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Serialize, Debug)]
pub struct JsonRpcResponse<'a, T> {
	/// JSON-RPC version.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub jsonrpc: TwoPointZero,
	/// Result.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub result: T,
	/// Request ID
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub id: Option<&'a RawValue>,
}

/// JSON-RPC error response object.
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Serialize, Debug)]
pub struct JsonRpcError<'a> {
	/// JSON-RPC version.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub jsonrpc: TwoPointZero,
	/// Error.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub error: JsonRpcErrorParams<'a>,
	/// Request ID
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub id: Option<&'a RawValue>,
}

/// [JSON-RPC error object](https://www.jsonrpc.org/specification#error-object)
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Serialize, Debug)]
pub struct JsonRpcErrorParams<'a> {
	/// Error code.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub code: i32,
	/// Error message.
Maciej Hirsz's avatar
Maciej Hirsz committed
	pub message: &'a str,
}

/// JSON-RPC v2 marker type.
Maciej Hirsz's avatar
Maciej Hirsz committed
#[derive(Debug, Default)]
pub struct TwoPointZero;

struct TwoPointZeroVisitor;

impl<'de> Visitor<'de> for TwoPointZeroVisitor {
	type Value = TwoPointZero;

	fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		formatter.write_str(r#"a string "2.0""#)
	}

	fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
	where
		E: de::Error,
	{
		match s {
			"2.0" => Ok(TwoPointZero),
			_ => Err(de::Error::invalid_value(Unexpected::Str(s), &self)),
		}
	}
}

impl<'de> Deserialize<'de> for TwoPointZero {
	fn deserialize<D>(deserializer: D) -> Result<TwoPointZero, D::Error>
	where
		D: Deserializer<'de>,
	{
		deserializer.deserialize_str(TwoPointZeroVisitor)
	}
}

impl Serialize for TwoPointZero {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: Serializer,
	{
		serializer.serialize_str("2.0")
	}
}

/// Parameters sent with the RPC request
#[derive(Clone, Copy)]
pub struct RpcParams<'a>(Option<&'a str>);

impl<'a> RpcParams<'a> {
	/// Create params
	pub fn new(raw: Option<&'a str>) -> Self {
		Self(raw)
	}

	/// Attempt to parse all parameters as array or map into type T
	pub fn parse<T>(self) -> Result<T, RpcError>
	where
		T: Deserialize<'a>,
	{
		match self.0 {
			None => Err(RpcError::InvalidParams),
			Some(params) => serde_json::from_str(params).map_err(|_| RpcError::InvalidParams),
		}
	}

	/// Attempt to parse only the first parameter from an array into type T
	pub fn one<T>(self) -> Result<T, RpcError>
	where
		T: Deserialize<'a>,
	{
		self.parse::<[T; 1]>().map(|[res]| res)
	}
}