#![allow(clippy::type_repetition_in_bounds)]
extern crate essrpc_macros;
pub use essrpc_macros::essrpc;
use std::fmt;
use serde::{Deserialize, Serialize};
#[cfg(feature = "async_client")]
use async_trait::async_trait;
#[cfg(feature = "async_client")]
use std::future::Future;
#[cfg(feature = "async_client")]
use std::pin::Pin;
pub mod transports;
type Result<T> = std::result::Result<T, RPCError>;
#[derive(Debug)]
pub struct MethodId {
pub name: &'static str,
pub num: u32,
}
#[derive(Debug)]
pub enum PartialMethodId {
Name(String),
Num(u32),
}
pub trait ClientTransport {
type TXState;
type FinalState;
fn tx_begin_call(&mut self, method: MethodId) -> Result<Self::TXState>;
fn tx_add_param(
&mut self,
name: &'static str,
value: impl Serialize,
state: &mut Self::TXState,
) -> Result<()>;
fn tx_finalize(&mut self, state: Self::TXState) -> Result<Self::FinalState>;
fn rx_response<T>(&mut self, state: Self::FinalState) -> Result<T>
where
for<'de> T: Deserialize<'de>;
}
#[cfg(feature = "async_client")]
#[async_trait]
pub trait AsyncClientTransport: Send {
type TXState: Send;
type FinalState: Send;
async fn tx_begin_call(&mut self, method: MethodId) -> Result<Self::TXState>;
async fn tx_add_param(
&mut self,
name: &'static str,
value: impl Serialize + Send + 'async_trait,
state: &mut Self::TXState,
) -> Result<()>;
async fn tx_finalize(&mut self, state: Self::TXState) -> Result<Self::FinalState>;
async fn rx_response<T>(&mut self, state: Self::FinalState) -> Result<T>
where
for<'de> T: Deserialize<'de>,
T: 'static;
}
pub trait ServerTransport {
type RXState;
fn rx_begin_call(&mut self) -> Result<(PartialMethodId, Self::RXState)>;
fn rx_read_param<T>(&mut self, name: &'static str, state: &mut Self::RXState) -> Result<T>
where
for<'de> T: serde::Deserialize<'de>;
fn tx_response(&mut self, value: impl Serialize) -> Result<()>;
}
pub trait RPCClient {
type TR: ClientTransport;
fn new(transform: Self::TR) -> Self;
}
#[cfg(feature = "async_client")]
pub trait AsyncRPCClient {
type TR: AsyncClientTransport;
fn new(transform: Self::TR) -> Self;
}
pub trait RPCServer {
fn serve_single_call(&mut self) -> Result<()>;
fn serve_until(&mut self, mut cond: impl FnMut() -> bool) -> Result<()> {
loop {
self.serve_single_call()?;
if !cond() {
return Ok(());
}
}
}
fn serve(&mut self) -> Result<()> {
loop {
self.serve_single_call()?;
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct GenericSerializableError {
description: String,
cause: Option<Box<GenericSerializableError>>,
}
impl GenericSerializableError {
pub fn new(e: impl std::error::Error) -> Self {
let cause = e
.source()
.map(|ec| Box::new(GenericSerializableError::from_dyn(ec)));
GenericSerializableError {
description: e.to_string(),
cause,
}
}
pub fn from_dyn(e: &dyn std::error::Error) -> Self {
let cause = e
.source()
.map(|ec| Box::new(GenericSerializableError::from_dyn(ec)));
GenericSerializableError {
description: e.to_string(),
cause,
}
}
}
impl std::error::Error for GenericSerializableError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
#[allow(clippy::match_as_ref)] match self.cause {
Some(ref e) => Some(e),
None => None,
}
}
}
impl fmt::Display for GenericSerializableError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.cause {
Some(ref e) => write!(f, "{} caused by:\n {}", self.description, e),
None => write!(f, "{}", self.description),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RPCError {
pub kind: RPCErrorKind,
msg: String,
cause: Option<Box<GenericSerializableError>>,
}
impl RPCError {
pub fn new(kind: RPCErrorKind, msg: impl Into<String>) -> Self {
RPCError {
kind,
msg: msg.into(),
cause: None,
}
}
pub fn with_cause(
kind: RPCErrorKind,
msg: impl Into<String>,
cause: impl std::error::Error,
) -> Self {
RPCError {
kind,
msg: msg.into(),
cause: Some(Box::new(GenericSerializableError::new(cause))),
}
}
pub fn cause(&self) -> Option<&GenericSerializableError> {
self.cause.as_ref().map(|boxed_e| boxed_e.as_ref())
}
}
impl fmt::Display for RPCError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.cause {
Some(ref e) => write!(f, "{} caused by:\n {}", self.msg, e),
None => write!(f, "{}", self.msg),
}
}
}
impl std::error::Error for RPCError {}
impl From<std::io::Error> for RPCError {
fn from(e: std::io::Error) -> RPCError {
match e.kind() {
std::io::ErrorKind::UnexpectedEof => {
RPCError::with_cause(RPCErrorKind::TransportEOF, "IO error in transport", e)
}
_ => RPCError::with_cause(RPCErrorKind::TransportError, "IO error in transport", e),
}
}
}
impl From<std::array::TryFromSliceError> for RPCError {
fn from(e: std::array::TryFromSliceError) -> RPCError {
RPCError::with_cause(RPCErrorKind::TransportError, "IO error in transport", e)
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub enum RPCErrorKind {
SerializationError,
UnknownMethod,
TransportError,
TransportEOF,
IllegalState,
Other,
}
#[cfg(feature = "async_client")]
pub type BoxFuture<T, E> = Pin<Box<dyn Future<Output = std::result::Result<T, E>>>>;
pub mod internal {
#[cfg(feature = "async_client")]
pub use async_trait::async_trait as rpc_async_trait;
#[cfg(feature = "async_client")]
pub use futures::lock::Mutex as AsyncMutex;
pub use parking_lot::Mutex as SyncMutex;
}