Skip to main content

ruststream_lapin/
publish_policy.rs

1//! The declaration half of publishing: the policies and what they pair into.
2//!
3//! A policy holds nothing but publish options, so it is constructible anywhere - in a router
4//! definition, in configuration, before anything connects. Pairing it with a
5//! [`ConnectedLapinBroker`] produces the live publisher (see [`crate::publisher`]), which is the
6//! only value with a publish surface. The publishing mode is a policy transition:
7//! [`LapinPublish::confirms`] and [`LapinPublish::server_tx`] move to the transactional policies,
8//! keeping the options.
9
10use ruststream::{PairError, PublishPolicy};
11
12use crate::broker::ConnectedLapinBroker;
13use crate::publisher::{ConfirmsPublisher, LapinPublisher, ServerTxPublisher};
14
15use self::sealed::Sealed;
16
17mod sealed {
18    /// Seals [`LapinPublishPolicy`](super::LapinPublishPolicy): pairing an AMQP publisher opens
19    /// no channel of its own, and the synchronous
20    /// [`publisher`](crate::ConnectedLapinBroker::publisher) accessor depends on that.
21    pub trait Sealed {}
22
23    impl Sealed for super::LapinPublish {}
24    impl Sealed for super::ConfirmsPublish {}
25    impl Sealed for super::ServerTxPublish {}
26    impl Sealed for crate::requester::LapinRequest {}
27}
28
29/// The options every `RabbitMQ` publish policy carries: where to publish and how durably.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub(crate) struct PublishOptions {
32    pub(crate) exchange: String,
33    pub(crate) persistent: bool,
34}
35
36impl Default for PublishOptions {
37    fn default() -> Self {
38        Self {
39            exchange: String::new(),
40            persistent: true,
41        }
42    }
43}
44
45/// A publish policy that pairs with a connected `RabbitMQ` broker without opening a channel.
46///
47/// All of this crate's policies hold nothing but publish options, so bringing one alive is a
48/// constructor call rather than broker work. That is what lets
49/// [`ConnectedLapinBroker::publisher`] be synchronous; [`PublishPolicy::pair`], the
50/// framework-side entry point, delegates here.
51pub trait LapinPublishPolicy: PublishPolicy<ConnectedLapinBroker> + Sealed {
52    /// Pairs the policy with the connected broker, producing the live publisher.
53    #[must_use]
54    fn bind(self, connected: &ConnectedLapinBroker) -> Self::Live;
55}
56
57/// The fire-and-forget publish policy: pure declaration, constructible anywhere.
58///
59/// [`OutgoingMessage::name`](ruststream::OutgoingMessage::name) is the routing key; the target
60/// exchange is a property of the policy (the default exchange unless
61/// [`exchange`](Self::exchange) says otherwise). On the default exchange the routing key
62/// addresses the queue with that name. Messages are published persistent (delivery mode 2)
63/// unless [`persistent(false)`](Self::persistent) opts out.
64///
65/// It pairs into [`LapinPublisher`], and it is the broker's
66/// [`DefaultPublish`](ruststream::DefaultPublish) policy, so a `publish("dest")` handler mounted
67/// without an explicit publisher replies through it. [`confirms`](Self::confirms) and
68/// [`server_tx`](Self::server_tx) move to the transactional policies, keeping the options.
69///
70/// # Examples
71///
72/// ```
73/// use ruststream_lapin::LapinPublish;
74///
75/// let events = LapinPublish::default().exchange("events");
76/// let shipments = LapinPublish::default().confirms();
77/// # let _ = (events, shipments);
78/// ```
79#[derive(Debug, Clone, Default, PartialEq, Eq)]
80#[must_use]
81pub struct LapinPublish(PublishOptions);
82
83impl LapinPublish {
84    /// Publishes to `exchange` instead of the default exchange.
85    pub fn exchange(mut self, exchange: impl Into<String>) -> Self {
86        self.0.exchange = exchange.into();
87        self
88    }
89
90    /// Whether messages are marked persistent (delivery mode 2). Defaults to `true`.
91    pub fn persistent(mut self, persistent: bool) -> Self {
92        self.0.persistent = persistent;
93        self
94    }
95
96    /// Moves to the policy that awaits broker confirms, with buffering transactions.
97    ///
98    /// The recommended transactional publisher: durable and much faster than AMQP server
99    /// transactions.
100    pub fn confirms(self) -> ConfirmsPublish {
101        ConfirmsPublish(self.0)
102    }
103
104    /// Moves to the policy backed by AMQP server transactions (`tx.select`).
105    ///
106    /// Server-side atomicity, at the cost of a synchronous commit round trip that is
107    /// significantly slower than [`confirms`](Self::confirms).
108    pub fn server_tx(self) -> ServerTxPublish {
109        ServerTxPublish(self.0)
110    }
111}
112
113impl PublishPolicy<ConnectedLapinBroker> for LapinPublish {
114    type Live = LapinPublisher;
115
116    async fn pair(self, connected: &ConnectedLapinBroker) -> Result<Self::Live, PairError> {
117        Ok(self.bind(connected))
118    }
119}
120
121impl LapinPublishPolicy for LapinPublish {
122    fn bind(self, connected: &ConnectedLapinBroker) -> Self::Live {
123        LapinPublisher::new(connected, self.0)
124    }
125}
126
127/// The confirm-transactional publish policy: same options as [`LapinPublish`], pairing into
128/// [`ConfirmsPublisher`].
129///
130/// Reached with [`LapinPublish::confirms`].
131///
132/// # Examples
133///
134/// ```
135/// use ruststream_lapin::LapinPublish;
136///
137/// let shipments = LapinPublish::default().exchange("shipments").confirms();
138/// # let _ = shipments;
139/// ```
140#[derive(Debug, Clone, Default, PartialEq, Eq)]
141#[must_use]
142pub struct ConfirmsPublish(PublishOptions);
143
144impl ConfirmsPublish {
145    /// Publishes to `exchange` instead of the default exchange.
146    pub fn exchange(mut self, exchange: impl Into<String>) -> Self {
147        self.0.exchange = exchange.into();
148        self
149    }
150
151    /// Whether messages are marked persistent (delivery mode 2). Defaults to `true`.
152    pub fn persistent(mut self, persistent: bool) -> Self {
153        self.0.persistent = persistent;
154        self
155    }
156}
157
158impl PublishPolicy<ConnectedLapinBroker> for ConfirmsPublish {
159    type Live = ConfirmsPublisher;
160
161    async fn pair(self, connected: &ConnectedLapinBroker) -> Result<Self::Live, PairError> {
162        Ok(self.bind(connected))
163    }
164}
165
166impl LapinPublishPolicy for ConfirmsPublish {
167    fn bind(self, connected: &ConnectedLapinBroker) -> Self::Live {
168        ConfirmsPublisher::new(connected, self.0)
169    }
170}
171
172/// The server-transactional publish policy: same options as [`LapinPublish`], pairing into
173/// [`ServerTxPublisher`].
174///
175/// Reached with [`LapinPublish::server_tx`].
176///
177/// # Examples
178///
179/// ```
180/// use ruststream_lapin::LapinPublish;
181///
182/// let ledger = LapinPublish::default().exchange("ledger").server_tx();
183/// # let _ = ledger;
184/// ```
185#[derive(Debug, Clone, Default, PartialEq, Eq)]
186#[must_use]
187pub struct ServerTxPublish(PublishOptions);
188
189impl ServerTxPublish {
190    /// Publishes to `exchange` instead of the default exchange.
191    pub fn exchange(mut self, exchange: impl Into<String>) -> Self {
192        self.0.exchange = exchange.into();
193        self
194    }
195
196    /// Whether messages are marked persistent (delivery mode 2). Defaults to `true`.
197    pub fn persistent(mut self, persistent: bool) -> Self {
198        self.0.persistent = persistent;
199        self
200    }
201}
202
203impl PublishPolicy<ConnectedLapinBroker> for ServerTxPublish {
204    type Live = ServerTxPublisher;
205
206    async fn pair(self, connected: &ConnectedLapinBroker) -> Result<Self::Live, PairError> {
207        Ok(self.bind(connected))
208    }
209}
210
211impl LapinPublishPolicy for ServerTxPublish {
212    fn bind(self, connected: &ConnectedLapinBroker) -> Self::Live {
213        ServerTxPublisher::new(connected, self.0)
214    }
215}