ezrpc 0.1.1

Ergonomic, flexible and Zero-cost RPC framework
Documentation
use anyhow::Context;
use rmp::decode;
use serde::{de::DeserializeOwned, Deserialize, Serializer};
use std::error::Error as StdError;
use std::ops::RangeFrom;

pub const REQUEST: u32 = 0; // Caller->Callee [REQUEST, ID: u32, METHOD: u32, ARGS: Any]
pub const RESPONSE: u32 = 1; // Callee->Caller [RESPONSE, ID: u32, ERROR: Option<String>, RESULT: Any]
pub const NOTIFY: u32 = 2; // Caller->Callee [NOTIFY, METHOD: u32, ARGS: Any]
pub const RESPONSE_STREAM: u32 = 3; // Callee->Caller [RESPONSE_STREAM, ID: u32, END: bool, RESULT: Any]

/// Represents a packet unit for ezrpc
pub struct Packet {
    pub(crate) meta: PackMeta,
    pack: Vec<u8>,
    dref: RangeFrom<usize>,
}

/// The type of RPC method id, is commonly string or integer, but can be any serializable types
pub type Method = serde_value::Value;

#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum PackMeta {
    Notify { method: Method },
    Request { method: Method, req: u32 },
    Response { req: u32, error: Option<Box<str>> },
    Stream { req: u32, finished: bool },
}

impl Packet {
    pub fn from_pack(pack: Vec<u8>) -> anyhow::Result<Self> {
        let mut reader = &pack[..];
        let start_ptr = reader.as_ptr() as usize;
        let len = decode::read_array_len(&mut reader).context("len")?;
        let pack_type: u32 = decode::read_int(&mut reader).context("type")?;

        Ok(match pack_type {
            REQUEST => {
                assert_eq!(len, 4);
                let req: u32 = decode::read_int(&mut reader).context("reqid")?;
                let method = rmp_serde::decode::from_read(&mut reader).context("method")?;

                Self {
                    meta: PackMeta::Request { method, req },
                    dref: reader.as_ptr() as usize - start_ptr..,
                    pack,
                }
            }
            NOTIFY => {
                assert_eq!(len, 3);
                let method = rmp_serde::decode::from_read(&mut reader).context("method")?;

                Self {
                    meta: PackMeta::Notify { method },
                    dref: reader.as_ptr() as usize - start_ptr..,
                    pack,
                }
            }
            RESPONSE => {
                assert_eq!(len, 4);
                let req: u32 = decode::read_int(&mut reader).context("reqid")?;
                let error = rmpv::decode::read_value(&mut reader).context("method")?;
                Self {
                    meta: PackMeta::Response {
                        req,
                        error: error.as_str().map(Into::into),
                    },
                    dref: reader.as_ptr() as usize - start_ptr..,
                    pack,
                }
            }
            RESPONSE_STREAM => {
                assert_eq!(len, 4);
                let req: u32 = decode::read_int(&mut reader).context("reqid")?;
                let finished = decode::read_bool(&mut reader).context("finished")?;
                Self {
                    meta: PackMeta::Stream { req, finished },
                    dref: reader.as_ptr() as usize - start_ptr..,
                    pack,
                }
            }
            _ => unreachable!(),
        })
    }

    /// Data part in the packet
    #[inline(always)]
    pub fn data(&self) -> &[u8] {
        &self.pack[self.dref.clone()]
    }

    /// Get error message in the response packet
    pub fn error(&self) -> Option<&str> {
        match &self.meta {
            PackMeta::Response { error, .. } => error.as_ref().map(AsRef::as_ref),
            _ => None,
        }
    }

    /// Deserialize the data in the packet
    #[inline(always)]
    pub fn decode<'a, T: Deserialize<'a>>(&'a self) -> Result<T, impl StdError> {
        rmp_serde::from_slice(self.data())
    }

    #[inline(always)]
    pub fn decode_to<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        serde_transcode::transcode(&mut rmp_serde::Deserializer::new(self.data()), s)
    }

    /// Get the RPC method in the notify/request packet
    pub fn method(&self) -> Option<&Method> {
        match &self.meta {
            PackMeta::Notify { method } => Some(method),
            PackMeta::Request { method, .. } => Some(method),
            _ => None,
        }
    }

    /// Get the request id
    pub fn request_id(&self) -> Option<u32> {
        match self.meta {
            PackMeta::Request { req, .. } => Some(req),
            PackMeta::Response { req, .. } => Some(req),
            _ => None,
        }
    }
}

/// Trait for types which can be converted by a Packet
pub trait FromPacket: Sized {
    fn from_packet(pack: Packet) -> anyhow::Result<Self>;
}

impl<T: DeserializeOwned + 'static> FromPacket for T {
    fn from_packet(pack: Packet) -> anyhow::Result<Self> {
        match pack.error() {
            Some(err) => Err(anyhow::Error::msg(err.to_string())),
            None => Ok(pack.decode()?),
        }
    }
}

impl FromPacket for Packet {
    fn from_packet(pack: Packet) -> anyhow::Result<Self> {
        Ok(pack)
    }
}