dnet-rpc 0.1.1

Remote Procedure Call over dnet transports
Documentation
#![warn(missing_docs)]

//! RPC over `dnet` transports.

pub mod consumer;
pub use consumer::{Consume, StreamRequest, ValueRequest};
use dnet_utils::ConditionalSend;

pub mod producer;

pub mod parts;

use std::fmt::{Debug, Display};
use std::future::Future;

pub use dportable::time::{sleep, Instant, Sleep, Timeout};
pub use dportable::{spawn, JoinHandle};

pub use dnet_base;

pub use atomic_counter;
pub use futures;

/// Macro for marking traits defining api's interface.
///
/// It will generate the following:
/// - `Consumer` struct implementing [Consume] trait - which can be used to make requests,
/// - `Request` enum - serializable structure representing request type with its arguments,
/// - `Response` enum - serializable structure representing response to a request,
/// - `impl_produce` macro which is used by [Produce] derive macro to implement
///   [producer::Produce] trait.
///
/// [Consume]: self::Consume
pub use dnet_macros::api;

/// Used together with [api] macro, disables attachment of [serde] `Serialize` and/or
/// `Deserialize` derive macros on generated `Request` and/or `Response` enums.
///
/// Following options are available:
/// - `request` - disables both `Serialize` and `Deserialize` for generated `Request` enum,
/// - `response` - disables both `Serialize` and `Deserialize` for generated `Response` enum,
/// - `request_serialize` - disables `Serialize` for generated `Request` enum,
/// - `request_deserialize` - disables `Deserialize` for generated `Request` enum,
/// - `response_serialize` - disables `Serialize` for generated `Response` enum,
/// - `response_deserialize` - disables `Deserialize` for generated `Response` enum.
///
/// Use without arguments - `#[no_serde]` - is equivalent to `#[no_serde(request, response)]`.
pub use dnet_macros::no_serde;

/// Marker for api functions that are "fire-and-forget" - they return as soon as request is
/// sent to the producer without waiting for response - in fact producer won't even send it.
///
/// It is useful for cases of one-directional communication where you don't care about
/// the producer finishing the task.
pub use dnet_macros::no_ack;

/// Marker for api functions that will provide producer with
/// [AbortionToken](crate::producer::abortable::AbortionToken)
/// which will be triggered when consumer aborts the request.
///
/// Abortion will be passed as the last argument of the producer method as
/// `abortion_token: AbortionToken`.
pub use dnet_macros::abortable;

/// Derive macro implementing [Produce] trait for struct.
///
/// **NOTE**: it depends on `impl_produce` macro, `Request` and `Response` enums
/// generated by the [api] macro being in scope.
///
/// [Produce]: self::producer::Produce
pub use dnet_macros::Produce;

/// Helper trait for consumer errors.
pub trait TransportError: ConditionalSend + 'static {}
impl<T> TransportError for T where T: ConditionalSend + 'static {}

/// Helper trait for transports used by `dnet-rpc`.
pub trait Transport<Incoming, Outgoing, Error>:
    dnet_base::Transport<Incoming, Outgoing, Error> + ConditionalSend + 'static
{
}
impl<T, Incoming, Outgoing, Error> Transport<Incoming, Outgoing, Error> for T where
    T: dnet_base::Transport<Incoming, Outgoing, Error> + ConditionalSend + 'static
{
}

/// Helper trait for shutdown futures used by producers and consumers.
pub trait Shutdown: Future<Output = ShutdownType> + ConditionalSend + Unpin + 'static {}
impl<T> Shutdown for T where T: Future<Output = ShutdownType> + ConditionalSend + Unpin + 'static {}

/// RPC error.
#[derive(Debug)]
pub enum Error {
    /// Connection closed.
    Closed,

    /// Request was aborted by consumer/producer.
    Aborted,

    /// Consumer/producer was shut down.
    ///
    /// **NOTE**: It may also be caused by connection being closed before consumer
    /// request was made.
    Shutdown,

    /// Consumer timed out.
    Timeout,

    /// Consumer was dropped before request could complete.
    Dropped,
}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Closed => write!(f, "transport closed"),
            Error::Aborted => write!(f, "request was aborted"),
            Error::Shutdown => write!(f, "producer/consumer was shutdown"),
            Error::Timeout => write!(f, "producer/consumer timed out"),
            Error::Dropped => write!(f, "consumer was dropped"),
        }
    }
}

impl std::error::Error for Error {}

/// Cause of producer/consumer shutdown.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShutdownType {
    /// Connection closed.
    Closed,

    /// Manual shutdown.
    Shutdown,

    /// Abort all requests.
    Aborted,

    /// Consumer/producer timed out.
    ///
    /// **NOTE**: Consumer requests will not receive [Error::Timeout] error
    /// (they will error out with other error type like [Error::Closed] or [Error::Shutdown]).
    /// This design is deliberate to not give consumers easily determinable info about timeout
    /// duration producer was configured with.
    Timeout,
}

impl From<ShutdownType> for Error {
    fn from(value: ShutdownType) -> Self {
        match value {
            ShutdownType::Closed => Error::Closed,
            ShutdownType::Shutdown => Error::Shutdown,
            ShutdownType::Aborted => Error::Aborted,
            ShutdownType::Timeout => Error::Timeout,
        }
    }
}