ump-ng 0.2.1

Micro message passing library for threads/tasks communication.
Documentation
//! Micro Message Pass: Next Generation (ump-ng) is a library for passing
//! messages between thread/tasks.  It is similar to the `ump` library, but
//! with an added uni-directional message passing primitive.
//!
//! The primary purpose of ump(-ng) is to create simple RPC-like designs, but
//! between threads/tasks within a process rather than between processes over
//! networks.
//!
//! # High-level usage overview
//! An application calls [`channel`] to create a linked pair of a [`Server`]
//! and a [`Client`].
//!
//! The server calls [`Server::wait()`]/[`Server::async_wait()`], which
//! blocks and waits for an incoming message from a client.
//!
//! A client, in a separate thread or task, calls [`Client::post()`] to send a
//! unidirectional message to the server, or [`Client::req()`]/
//! [`Client::areq()`] to send a message to the server and wait for a reply.
//!
//! The server's wait call returns either a _post_ message or a _request_
//! message that consist a pair of a message and a [`ReplyContext`] that is
//! used to send a reply back to the client.
//!
//! After processing its application-defined message, the server *must* call
//! the [`ReplyContext::reply()`] on the returned reply context object to
//! return a reply message to the client.
//!
//! Typically the server calls wait again to wait for next message from a
//! client.
//!
//! The client receives the reply from the server and processes it.
//!
//! # Example
//! ```
//! use std::thread;
//!
//! use ump_ng::{channel, MsgType};
//!
//! let (server, client) = channel::<String, String, String, ()>();
//!
//! let server_thread = thread::spawn(move || {
//!   // Wait for data to arrive from a client
//!   loop {
//!     println!("Server waiting for message ..");
//!     match server.wait().unwrap() {
//!       MsgType::Post(data) => {
//!         println!("Server received Post: '{}'", data);
//!       }
//!       MsgType::Request(data, rctx) => {
//!         println!("Server received Request: '{}'", data);
//!
//!         // Process data from client
//!
//!         // Reply to client
//!         let reply = format!("Hello, {}!", data);
//!         println!("Server replying '{}'", reply);
//!         rctx.reply(reply);
//!         break;
//!       }
//!     }
//!   }
//!
//!   println!("Server done");
//! });
//!
//! let msg = String::from("Client");
//! println!("Client putting '{}'", msg);
//! let reply = client.post(msg).unwrap();
//!
//! let msg = String::from("Client");
//! println!("Client requesting '{}'", msg);
//! let reply = client.req(msg).unwrap();
//! println!("Client received reply '{}'", reply);
//!
//! println!("Client done");
//!
//! server_thread.join().unwrap();
//! ```
//! In practice the send/reply types will probably be `enum`s used to
//! indicate command/return type with associated data.  The third type argument
//! to [`channel`] is an error type that can be used to explicitly pass errors
//! back to the sender.
//!
//! # Semantics
//! There are some potentially useful semantic quirks that can be good to know
//! about, but some of them should be used with caution.  This section will
//! describe some semantics that you can rely on, and others that you should be
//! careful about relying on.
//!
//! ## Stable invariants
//!
//! These are behaviors which should not change in future versions.
//!
//! - The reply contexts are independent of the `Server` context.  This has
//!   some useful implications for server threads that spawn separate threads
//!   to process messages and return replies:  *The server can safely terminate
//!   while there are clients waiting for replies* (implied: the server can
//!   safely terminate while there are reply contexts in-flight).
//! - A cloned client is paired with the same server as its origin, but in all
//!   other respects the clone and its origin are independent of each other.
//! - A client can be moved to a new thread.
//! - Any permutation of sync/async server/clients can be combined.  `async`
//!   code must use the async method variants when available.
//!
//! ## Unstable invariants
//!
//! These are invariants you can trust will work in the current version, but
//! they exist merely as a side-effect of the current implementation.  Avoid
//! relying on these if possible.
//!
//! - A single client can be used from two different threads.  If a `Client`
//!   object in placed in an Arc, is cloned and passed to another thread/task
//!   then both the clone and the original can be used simultaneously.  In the
//!   future this may not be allowed. It is recommended that a new clone of the
//!   client be created instead.
//! - Put/Request messages arrive in the same order they were added to the
//!   queue.  In future versions one type may be prioritized over the other.

mod client;
mod err;
mod rctx;
mod server;

pub use err::Error;

pub use crate::{
  client::{Client, Post as PostClient, WaitReply, Weak as WeakClient},
  rctx::ReplyContext,
  server::{MsgType, Server}
};

/// Create a pair of linked [`Server`] and [`Client`] objects.
///
/// The [`Server`] object is used to wait for incoming messages from connected
/// clients.  Once a message arrives it must reply to it using a
/// [`ReplyContext`] that's returned to it in the same call that returned the
/// message.
///
/// The [`Client`] object can be used to send messages to the [`Server`].  The
/// [`Client::req()`] call will not return until the server has replied.
///
/// Clients can be [cloned](Client::clone()); each clone will create a
/// new client object that is connected to the same server object, but is
/// completely independent of the original client.
///
/// The `S` type parameter is the "send" data type that clients will transfer
/// to the server.  The `R` type parameter is the "receive" data type that
/// clients will receive from the server.  The `E` type parameter can be used
/// to return application specific errors from the server to the client.
#[allow(clippy::type_complexity)]
#[must_use]
pub fn channel<P, S, R, E>() -> (Server<P, S, R, E>, Client<P, S, R, E>) {
  let (qpusher, qpuller) = sigq::new();

  (Server(qpuller), Client(qpusher))
}

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