veilid-core 0.5.3

Core library used to create a Veilid node and operate it as part of an application
Documentation
use super::*;

#[derive(ThisError, Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
#[must_use]
pub(crate) enum RPCError {
    #[error("[RPCError: Unimplemented({0})]")]
    Unimplemented(String),
    #[error("[RPCError: InvalidFormat({0})]")]
    InvalidFormat(String),
    #[error("[RPCError: Protocol({0})]")]
    Protocol(String),
    #[error("[RPCError: Internal({0})]")]
    Internal(String),
    #[error("[RPCError: Network({0})]")]
    Network(String),
    #[error("[RPCError: TryAgain({0})]")]
    TryAgain(String),
    #[error("[RPCError: Ignore({0})]")]
    Ignore(String),
}

impl RPCError {
    #[expect(dead_code)]
    pub fn unimplemented<X: ToString>(x: X) -> Self {
        Self::Unimplemented(x.to_string())
    }
    pub fn invalid_format<X: ToString>(x: X) -> Self {
        Self::InvalidFormat(x.to_string())
    }
    pub fn map_invalid_format<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
        move |x| Self::InvalidFormat(format!("{}: {}", message.to_string(), x.to_string()))
    }
    pub fn protocol<X: ToString>(x: X) -> Self {
        Self::Protocol(x.to_string())
    }
    pub fn map_protocol<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
        move |x| Self::Protocol(format!("{}: {}", message.to_string(), x.to_string()))
    }
    pub fn internal<X: ToString>(x: X) -> Self {
        Self::Internal(x.to_string())
    }
    pub fn map_internal<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
        move |x| Self::Internal(format!("{}: {}", message.to_string(), x.to_string()))
    }
    pub fn else_internal<M: ToString>(message: M) -> impl FnOnce() -> Self {
        move || Self::Internal(message.to_string())
    }
    pub fn network<X: ToString>(x: X) -> Self {
        Self::Network(x.to_string())
    }
    #[expect(dead_code)]
    pub fn map_network<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
        move |x| Self::Network(format!("{}: {}", message.to_string(), x.to_string()))
    }
    pub fn try_again<X: ToString>(x: X) -> Self {
        Self::TryAgain(x.to_string())
    }
    pub fn map_try_again<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
        move |x| Self::TryAgain(format!("{}: {}", message.to_string(), x.to_string()))
    }
    pub fn ignore<X: ToString>(x: X) -> Self {
        Self::Ignore(x.to_string())
    }
    #[expect(dead_code)]
    pub fn map_ignore<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
        move |x| Self::Ignore(format!("{}: {}", message.to_string(), x.to_string()))
    }
    pub fn else_ignore<M: ToString>(message: M) -> impl FnOnce() -> Self {
        move || Self::Ignore(message.to_string())
    }
}

impl From<RPCError> for VeilidAPIError {
    fn from(e: RPCError) -> Self {
        match e {
            RPCError::Unimplemented(message) => VeilidAPIError::Unimplemented { message },
            RPCError::InvalidFormat(message) => VeilidAPIError::Generic { message },
            RPCError::Protocol(message) => VeilidAPIError::Generic { message },
            RPCError::Internal(message) => VeilidAPIError::Internal { message },
            RPCError::Network(message) => VeilidAPIError::Generic { message },
            RPCError::TryAgain(message) => VeilidAPIError::TryAgain { message },
            RPCError::Ignore(message) => VeilidAPIError::Generic { message },
        }
    }
}

pub type RPCNetworkResult<T> = Result<NetworkResult<T>, RPCError>;

pub(crate) trait ToRPCNetworkResult<T> {
    fn to_rpc_network_result(self) -> RPCNetworkResult<T>;
}

impl<T> ToRPCNetworkResult<T> for VeilidAPIResult<T> {
    fn to_rpc_network_result(self) -> RPCNetworkResult<T> {
        match self {
            Err(VeilidAPIError::TryAgain { message }) => Err(RPCError::TryAgain(message)),
            Err(VeilidAPIError::Timeout) => Ok(NetworkResult::timeout()),
            Err(VeilidAPIError::Unimplemented { message }) => Err(RPCError::Unimplemented(message)),
            Err(e) => Err(RPCError::internal(e)),
            Ok(v) => Ok(NetworkResult::value(v)),
        }
    }
}

impl From<capnp::NotInSchema> for RPCError {
    fn from(_value: capnp::NotInSchema) -> Self {
        RPCError::ignore("not in schema")
    }
}

impl From<capnp::Error> for RPCError {
    fn from(value: capnp::Error) -> Self {
        RPCError::protocol(value)
    }
}

pub trait RPCErrorIgnoreOk<T> {
    fn ignore_ok(self) -> Result<Option<T>, RPCError>;
}

impl<T> RPCErrorIgnoreOk<T> for Result<T, RPCError> {
    fn ignore_ok(self) -> Result<Option<T>, RPCError> {
        match self {
            Ok(v) => Ok(Some(v)),
            Err(RPCError::Ignore(_)) => Ok(None),
            Err(e) => Err(e),
        }
    }
}

#[macro_export]
macro_rules! rpc_ignore_missing_property {
    ($reader:expr, $propname:tt) => {
        pastey::paste! {
            if !$reader.[<has_ $propname>]() {
                return Err(RPCError::ignore(concat!("missing ", stringify!($propname))));
            }
        }
    };
}

#[macro_export]
macro_rules! rpc_ignore_max_len {
    ($reader:expr, $max_len:expr) => {{
        let _len = $reader.len() as usize;
        if _len > $max_len {
            return Err(RPCError::ignore(concat!(
                stringify!($reader),
                " length > ",
                stringify!($max_len)
            )));
        }
        _len
    }};
}

#[macro_export]
macro_rules! rpc_ignore_min_max_len {
    ($reader:expr, $min_len:expr, $max_len:expr) => {{
        let _len = $reader.len() as usize;
        if _len < $min_len {
            return Err(RPCError::ignore(concat!(
                stringify!($reader),
                " length < ",
                stringify!($min_len)
            )));
        }
        if _len > $max_len {
            return Err(RPCError::ignore(concat!(
                stringify!($reader),
                " length > ",
                stringify!($max_len)
            )));
        }
        _len
    }};
}