ump_ng/
server.rs

1use crate::{
2  err::Error,
3  rctx::{RCtxState, ReplyContext}
4};
5
6pub enum InnerMsgType<P, S, R, E> {
7  Post(P),
8  Request(S, swctx::SetCtx<R, RCtxState, E>)
9}
10
11/// Mesage operation types.
12pub enum MsgType<P, S, R, E> {
13  /// A uni-directional message pass.
14  Post(P),
15
16  /// A message pass that expects a reply.
17  Request(S, ReplyContext<R, E>)
18}
19
20impl<P, S, R, E> TryFrom<InnerMsgType<P, S, R, E>> for MsgType<P, S, R, E> {
21  type Error = Error<E>;
22
23  fn try_from(val: InnerMsgType<P, S, R, E>) -> Result<Self, Self::Error> {
24    match val {
25      InnerMsgType::Post(msg) => Ok(Self::Post(msg)),
26      InnerMsgType::Request(msg, irctx) => {
27        // Create an application reply context from the reply context in the
28        // queue Implicitly changes state of the reply context from
29        // Queued to Waiting
30        let rctx = ReplyContext::try_from(irctx)?;
31
32        Ok(Self::Request(msg, rctx))
33      }
34    }
35  }
36}
37
38/// Representation of a server object.
39///
40/// Each instantiation of a [`Server`] object represents an end-point which
41/// will be used to receive messages from connected [`Client`](crate::Client)
42/// objects.
43#[repr(transparent)]
44pub struct Server<P, S, R, E>(
45  pub(crate) sigq::Puller<InnerMsgType<P, S, R, E>>
46);
47
48impl<P, S, R, E> Server<P, S, R, E>
49where
50  P: 'static + Send,
51  S: 'static + Send,
52  R: 'static + Send,
53  E: 'static + Send
54{
55  /// Block and wait, indefinitely, for an incoming message from a
56  /// [`Client`](crate::Client).
57  ///
58  /// Returns the message sent by the client and a reply context.  The server
59  /// must call [`ReplyContext::reply()`] on the reply context to pass a return
60  /// value to the client.
61  ///
62  /// # Errors
63  /// [`Error::ClientsDisappeared`] indicates that the queue is empty and
64  /// all the client end-points have been dropped.
65  pub fn wait(&self) -> Result<MsgType<P, S, R, E>, Error<E>> {
66    let msg = self.0.pop().map_err(|_| Error::ClientsDisappeared)?;
67    msg.try_into()
68  }
69
70  /// Take next next message off queue or return `None` is queue is empty.
71  ///
72  /// # Errors
73  /// [`Error::ClientsDisappeared`] indicates that the queue is empty and
74  /// all the client end-points have been dropped.
75  #[allow(clippy::type_complexity)]
76  pub fn try_pop(&self) -> Result<Option<MsgType<P, S, R, E>>, Error<E>> {
77    let msg = self.0.try_pop().map_err(|_| Error::ClientsDisappeared)?;
78    if let Some(msg) = msg {
79      Ok(Some(msg.try_into()?))
80    } else {
81      Ok(None)
82    }
83  }
84
85  /// Same as [`Server::wait()`], but for use in an `async` context.
86  #[allow(clippy::missing_errors_doc)]
87  pub async fn async_wait(&self) -> Result<MsgType<P, S, R, E>, Error<E>> {
88    let msg = self.0.apop().await.map_err(|_| Error::ClientsDisappeared)?;
89    msg.try_into()
90  }
91
92  /// Returns a boolean indicating whether the queue is/was empty.  This isn't
93  /// really useful unless used in very specific situations.  It mostly exists
94  /// for test cases.
95  #[must_use]
96  pub fn was_empty(&self) -> bool {
97    self.0.was_empty()
98  }
99}
100
101// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :