ump 0.13.0

Micro message passing library for threads/tasks communication.
Documentation
use crate::{
  err::Error,
  rctx::{RCtxState, ReplyContext}
};

pub struct QueueNode<S, R, E> {
  /// Raw message being sent from the client to the server.
  pub(crate) msg: S,

  /// Keep track of data needed to share reply data.
  pub(crate) reply: swctx::SetCtx<R, RCtxState, E>
}

/// Representation of a server object.
///
/// Each instantiation of a [`Server`] object represents an end-point which
/// will be used to receive messages from connected [`Client`](crate::Client)
/// objects.
#[repr(transparent)]
pub struct Server<S, R, E>(pub(crate) sigq::Puller<QueueNode<S, R, E>>);

impl<S, R, E> Server<S, R, E>
where
  S: 'static + Send,
  R: 'static + Send,
  E: 'static + Send
{
  /// Block and wait, indefinitely, for an incoming message from a
  /// [`Client`](crate::Client).
  ///
  /// Returns the message sent by the client and a reply context.  The server
  /// must call [`ReplyContext::reply()`] on the reply context to pass a return
  /// value to the client.
  ///
  /// # Errors
  /// `Err(Error::ClientsDisappeared)` indicates that the queue is empty and
  /// all the client end-points have been dropped.
  pub fn wait(&self) -> Result<(S, ReplyContext<R, E>), Error<E>> {
    let node = self.0.pop().map_err(|_| Error::ClientsDisappeared)?;

    // Extract the data from the node
    let msg = node.msg;

    // Create an application reply context from the reply context in the queue
    // Implicitly changes state of the reply context from Queued to Waiting
    let rctx = ReplyContext::try_from(node.reply)?;

    Ok((msg, rctx))
  }

  /// Take next next message off queue or return `None` is queue is empty.
  ///
  /// # Errors
  /// [`Error::ClientsDisappeared`] indicates that the queue is empty and
  /// all the client end-points have been dropped.
  #[allow(clippy::type_complexity)]
  pub fn try_pop(&self) -> Result<Option<(S, ReplyContext<R, E>)>, Error<E>> {
    let node = self.0.try_pop().map_err(|_| Error::ClientsDisappeared)?;

    if let Some(node) = node {
      // Extract the data from the node
      let msg = node.msg;

      // Create an application reply context from the reply context in the
      // queue Implicitly changes state of the reply context from Queued
      // to Waiting
      let rctx = ReplyContext::try_from(node.reply)?;

      Ok(Some((msg, rctx)))
    } else {
      Ok(None)
    }
  }

  /// Same as [`Server::wait()`], but for use in an `async` context.
  #[allow(clippy::missing_errors_doc)]
  pub async fn async_wait(&self) -> Result<(S, ReplyContext<R, E>), Error<E>> {
    let node = self.0.apop().await.map_err(|_| Error::ClientsDisappeared)?;

    // Extract the data from the node
    let msg = node.msg;

    // Create an application reply context from the reply context in the queue
    // Implicitly changes state of the reply context from Queued to Waiting
    let rctx = ReplyContext::try_from(node.reply)?;

    Ok((msg, rctx))
  }

  /// Returns a boolean indicating whether the queue is/was empty.  This isn't
  /// really useful unless used in very specific situations.  It mostly exists
  /// for test cases.
  #[must_use]
  pub fn was_empty(&self) -> bool {
    self.0.was_empty()
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :