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 :