Skip to main content

cyclonedds/
participant.rs

1use crate::Result;
2use crate::internal::ffi;
3use crate::internal::traits::AsFfi;
4
5/// A domain participant.
6///
7/// A participant is the entry point for all DDS communication within a
8/// [`Domain`](crate::Domain). All other entities, including topics, readers,
9/// writers, publishers, and subscribers, are created under a participant and
10/// are scoped to its lifetime.
11///
12/// Use [`Participant::new`] for simple construction or [`Participant::builder`]
13/// for [`QoS`](crate::QoS) and [`listener`](crate::listener::Listener)
14/// configuration.
15#[derive(Debug)]
16pub struct Participant<'domain> {
17    pub(crate) inner: cyclonedds_sys::dds_entity_t,
18    phantom: std::marker::PhantomData<&'domain crate::Domain>,
19}
20
21/// Builder for [`Participant`] (accessible via [`Participant::builder`]).
22#[derive(Debug)]
23pub struct ParticipantBuilder<'domain, 'qos> {
24    domain: &'domain crate::Domain,
25    qos: Option<&'qos crate::QoS>,
26    listener: Option<crate::Listener>,
27}
28
29impl<'d, 'q> ParticipantBuilder<'d, 'q> {
30    /// Creates a new [`ParticipantBuilder`] for the given
31    /// [`Domain`](crate::Domain).
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use cyclonedds::Domain;
37    /// use cyclonedds::builder::ParticipantBuilder;
38    ///
39    /// let domain = Domain::default();
40    /// let participant_builder = ParticipantBuilder::new(&domain);
41    /// ```
42    #[must_use]
43    pub const fn new(domain: &'d crate::Domain) -> Self {
44        Self {
45            domain,
46            qos: None,
47            listener: None,
48        }
49    }
50
51    /// Sets the [`QoS`](crate::QoS) for this participant builder.
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use cyclonedds::builder::ParticipantBuilder;
57    /// use cyclonedds::qos::policy;
58    /// use cyclonedds::{Duration, QoS};
59    /// # use cyclonedds::Domain;
60    /// # let domain = Domain::default();
61    ///
62    /// let qos = QoS::new().with_reliability(policy::Reliability::Reliable {
63    ///     max_blocking_time: Duration::from_millis(100),
64    /// });
65    /// let participant_builder = ParticipantBuilder::new(&domain).with_qos(&qos);
66    /// ```
67    #[must_use]
68    pub const fn with_qos(mut self, qos: &'q crate::QoS) -> Self {
69        self.qos = Some(qos);
70        self
71    }
72
73    /// Sets the [`Listener`](crate::Listener) on this participant builder.
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// use cyclonedds::Listener;
79    /// use cyclonedds::builder::ParticipantBuilder;
80    /// # use cyclonedds::Domain;
81    /// # let domain = Domain::default();
82    ///
83    /// let participant_builder = ParticipantBuilder::new(&domain).with_listener(Listener::new());
84    /// ```
85    #[must_use]
86    pub fn with_listener<L>(mut self, listener: L) -> Self
87    where
88        L: AsRef<crate::Listener>,
89    {
90        self.listener = Some(*listener.as_ref());
91        self
92    }
93
94    /// Builds the [`Participant`].
95    ///
96    /// # Errors
97    ///
98    /// Returns an [`Error`](crate::Error) if the participant failed to create.
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use cyclonedds::builder::ParticipantBuilder;
104    /// use cyclonedds::qos::policy::Durability;
105    /// use cyclonedds::{Domain, QoS};
106    ///
107    /// let domain = Domain::default();
108    /// let qos = QoS::new().with_durability(Durability::TransientLocal);
109    /// let participant = ParticipantBuilder::new(&domain).with_qos(&qos).build()?;
110    ///
111    /// # Ok::<_, cyclonedds::Error>(())
112    /// ```
113    pub fn build(self) -> Result<Participant<'d>> {
114        // NOTE: using `and_then` to avoid ? branch on the listener for coverage
115        // since the C lib currently panics on OOM rather than returning null.
116        self.listener
117            .map(|listener| listener.as_ffi())
118            .transpose()
119            .and_then(|listener| {
120                Ok(Participant {
121                    inner: ffi::dds_create_participant(
122                        self.domain.id,
123                        self.qos.map(|qos| &qos.inner),
124                        listener.as_ref(),
125                    )?,
126                    phantom: std::marker::PhantomData,
127                })
128            })
129    }
130}
131
132impl<'d> Participant<'d> {
133    /// Creates a new participant in the given [`Domain`](crate::Domain) with
134    /// default [`QoS`](crate::QoS) and no
135    /// [`listener`](crate::listener::Listener).
136    ///
137    /// # Errors
138    ///
139    /// Returns an [`Error`](crate::Error) if the participant fails to create.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use cyclonedds::{Domain, Participant};
145    ///
146    /// let domain = Domain::default();
147    /// let participant = Participant::new(&domain)?;
148    /// # Ok::<_, cyclonedds::Error>(())
149    /// ```
150    pub fn new(domain: &'d crate::Domain) -> Result<Self> {
151        Self::builder(domain).build()
152    }
153
154    /// Returns a [`ParticipantBuilder`] for constructing a participant with
155    /// custom [`QoS`](crate::QoS) or a [`listener`](crate::listener::Listener).
156    //
157    /// # Examples
158    ///
159    /// ```
160    /// use cyclonedds::{Domain, Participant};
161    ///
162    /// let domain = Domain::default();
163    /// let participant = Participant::builder(&domain).build()?;
164    /// # Ok::<_, cyclonedds::Error>(())
165    /// ```
166    #[must_use]
167    pub const fn builder<'q>(domain: &'d crate::Domain) -> ParticipantBuilder<'d, 'q> {
168        ParticipantBuilder::new(domain)
169    }
170
171    /// Sets the [`Listener`](crate::Listener) on this participant, replacing
172    /// any previously set listener.
173    ///
174    /// # Errors
175    ///
176    /// Returns an [`Error`](crate::Error) if the listener fails to set.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use cyclonedds::{Domain, Listener, Participant, listener::SubscriberListener};
182    ///
183    /// let domain = Domain::default();
184    /// let mut participant = Participant::new(&domain)?;
185    /// let listener =
186    ///     Listener::new().with_subscriber(|s| s.with_data_on_readers(|_| println!("data available")));
187    /// participant.set_listener(listener)?;
188    /// # Ok::<_, cyclonedds::Error>(())
189    /// ```
190    pub fn set_listener<L>(&mut self, listener: L) -> Result<()>
191    where
192        L: AsRef<crate::Listener>,
193    {
194        listener
195            .as_ref()
196            .as_ffi()
197            .and_then(|listener| ffi::dds_set_listener(self.inner, Some(listener.inner)))
198    }
199
200    /// Removes the listener from this participant.
201    ///
202    /// # Errors
203    ///
204    /// Returns an [`Error`](crate::Error) if the listener fails to unset.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// use cyclonedds::{Domain, Participant};
210    ///
211    /// let domain = Domain::default();
212    /// let mut participant = Participant::new(&domain)?;
213    /// participant.unset_listener()?;
214    /// # Ok::<_, cyclonedds::Error>(())
215    /// ```
216    pub fn unset_listener(&mut self) -> Result<()> {
217        ffi::dds_set_listener(self.inner, None)?;
218        Ok(())
219    }
220
221    /// Sets the [`Listener`](crate::Listener) on this participant, consuming
222    /// and returning `self`.
223    ///
224    /// Useful for chaining participant construction with listener
225    /// configuration.
226    ///
227    /// # Errors
228    ///
229    /// Returns an [`Error`](crate::Error) if the listener fails to set.
230    ///
231    /// # Examples
232    ///
233    /// ```
234    /// use cyclonedds::{Domain, Listener, Participant};
235    ///
236    /// let domain = Domain::default();
237    /// let participant = Participant::new(&domain)?.with_listener(Listener::new())?;
238    /// # Ok::<_, cyclonedds::Error>(())
239    /// ```
240    pub fn with_listener<L>(mut self, listener: L) -> Result<Self>
241    where
242        L: AsRef<crate::Listener>,
243    {
244        self.set_listener(listener).map(|()| self)
245    }
246}
247
248impl Drop for Participant<'_> {
249    fn drop(&mut self) {
250        let result = ffi::dds_delete(self.inner);
251        debug_assert!(
252            result.is_ok(),
253            "unable to delete {self:?}: failed with {result:?}"
254        );
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crate::Error;
262
263    #[test]
264    fn test_participant_create() {
265        let domain_id = crate::tests::domain::unique_id();
266        let domain = crate::Domain::new(domain_id).unwrap();
267
268        let qos = crate::QoS::new();
269
270        let _ = Participant::new(&domain).unwrap();
271        let _ = Participant::builder(&domain)
272            .with_qos(&qos)
273            .build()
274            .unwrap();
275        let _ = Participant::new(&domain).unwrap();
276        let _ = Participant::builder(&domain)
277            .with_qos(&qos)
278            .build()
279            .unwrap();
280    }
281
282    #[test]
283    fn test_participant_create_in_impossible_domain() {
284        let domain = crate::Domain {
285            id: u32::from(u16::MAX),
286            inner: 0,
287        };
288
289        let result = Participant::new(&domain).unwrap_err();
290        assert_eq!(result, Error::NonSpecific);
291
292        let qos = crate::QoS::new();
293        let result = Participant::builder(&domain)
294            .with_qos(&qos)
295            .build()
296            .unwrap_err();
297        assert_eq!(result, Error::NonSpecific);
298    }
299
300    #[test]
301    fn test_participant_with_listener() {
302        let domain_id = crate::tests::domain::unique_id();
303        let domain = crate::Domain::new(domain_id).unwrap();
304
305        let listener = crate::Listener::new();
306
307        let _ = Participant::new(&domain)
308            .unwrap()
309            .with_listener(listener)
310            .unwrap();
311        let _ = Participant::builder(&domain)
312            .with_listener(listener)
313            .build()
314            .unwrap();
315
316        let mut participant = Participant::new(&domain).unwrap();
317        participant.set_listener(listener).unwrap();
318        participant.unset_listener().unwrap();
319    }
320
321    #[test]
322    fn test_participant_with_listener_on_invalid_participant() {
323        let domain_id = crate::tests::domain::unique_id();
324        let domain = crate::Domain::new(domain_id).unwrap();
325
326        let listener = crate::Listener::new();
327
328        let mut participant = Participant::new(&domain).unwrap();
329        let participant_id = participant.inner;
330        participant.inner = 0;
331        let result = participant.set_listener(listener).unwrap_err();
332        assert_eq!(result, crate::Error::BadParameter);
333        let result = participant.unset_listener().unwrap_err();
334        assert_eq!(result, crate::Error::BadParameter);
335        participant.inner = participant_id;
336    }
337}