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::listener::SubscriberListener;
182    /// use cyclonedds::{Domain, Listener, Participant};
183    ///
184    /// let domain = Domain::default();
185    /// let mut participant = Participant::new(&domain)?;
186    /// let listener =
187    ///     Listener::new().with_subscriber(|s| s.with_data_on_readers(|_| println!("data available")));
188    /// participant.set_listener(listener)?;
189    /// # Ok::<_, cyclonedds::Error>(())
190    /// ```
191    pub fn set_listener<L>(&mut self, listener: L) -> Result<()>
192    where
193        L: AsRef<crate::Listener>,
194    {
195        listener
196            .as_ref()
197            .as_ffi()
198            .and_then(|listener| ffi::dds_set_listener(self.inner, Some(listener.inner)))
199    }
200
201    /// Removes the listener from this participant.
202    ///
203    /// # Errors
204    ///
205    /// Returns an [`Error`](crate::Error) if the listener fails to unset.
206    ///
207    /// # Examples
208    ///
209    /// ```
210    /// use cyclonedds::{Domain, Participant};
211    ///
212    /// let domain = Domain::default();
213    /// let mut participant = Participant::new(&domain)?;
214    /// participant.unset_listener()?;
215    /// # Ok::<_, cyclonedds::Error>(())
216    /// ```
217    pub fn unset_listener(&mut self) -> Result<()> {
218        ffi::dds_set_listener(self.inner, None)?;
219        Ok(())
220    }
221
222    /// Sets the [`Listener`](crate::Listener) on this participant, consuming
223    /// and returning `self`.
224    ///
225    /// Useful for chaining participant construction with listener
226    /// configuration.
227    ///
228    /// # Errors
229    ///
230    /// Returns an [`Error`](crate::Error) if the listener fails to set.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use cyclonedds::{Domain, Listener, Participant};
236    ///
237    /// let domain = Domain::default();
238    /// let participant = Participant::new(&domain)?.with_listener(Listener::new())?;
239    /// # Ok::<_, cyclonedds::Error>(())
240    /// ```
241    pub fn with_listener<L>(mut self, listener: L) -> Result<Self>
242    where
243        L: AsRef<crate::Listener>,
244    {
245        self.set_listener(listener).map(|()| self)
246    }
247}
248
249impl Drop for Participant<'_> {
250    fn drop(&mut self) {
251        let result = ffi::dds_delete(self.inner);
252        debug_assert!(
253            result.is_ok(),
254            "unable to delete {self:?}: failed with {result:?}"
255        );
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262    use crate::Error;
263
264    #[test]
265    fn test_participant_create() {
266        let domain_id = crate::tests::domain::unique_id();
267        let domain = crate::Domain::new(domain_id).unwrap();
268
269        let qos = crate::QoS::new();
270
271        let _ = Participant::new(&domain).unwrap();
272        let _ = Participant::builder(&domain)
273            .with_qos(&qos)
274            .build()
275            .unwrap();
276        let _ = Participant::new(&domain).unwrap();
277        let _ = Participant::builder(&domain)
278            .with_qos(&qos)
279            .build()
280            .unwrap();
281    }
282
283    #[test]
284    fn test_participant_create_in_impossible_domain() {
285        let domain = crate::Domain {
286            id: u32::from(u16::MAX),
287            inner: 0,
288        };
289
290        let result = Participant::new(&domain).unwrap_err();
291        assert_eq!(result, Error::NonSpecific);
292
293        let qos = crate::QoS::new();
294        let result = Participant::builder(&domain)
295            .with_qos(&qos)
296            .build()
297            .unwrap_err();
298        assert_eq!(result, Error::NonSpecific);
299    }
300
301    #[test]
302    fn test_participant_with_listener() {
303        let domain_id = crate::tests::domain::unique_id();
304        let domain = crate::Domain::new(domain_id).unwrap();
305
306        let listener = crate::Listener::new();
307
308        let _ = Participant::new(&domain)
309            .unwrap()
310            .with_listener(listener)
311            .unwrap();
312        let _ = Participant::builder(&domain)
313            .with_listener(listener)
314            .build()
315            .unwrap();
316
317        let mut participant = Participant::new(&domain).unwrap();
318        participant.set_listener(listener).unwrap();
319        participant.unset_listener().unwrap();
320    }
321
322    #[test]
323    fn test_participant_with_listener_on_invalid_participant() {
324        let domain_id = crate::tests::domain::unique_id();
325        let domain = crate::Domain::new(domain_id).unwrap();
326
327        let listener = crate::Listener::new();
328
329        let mut participant = Participant::new(&domain).unwrap();
330        let participant_id = participant.inner;
331        participant.inner = 0;
332        let result = participant.set_listener(listener).unwrap_err();
333        assert_eq!(result, crate::Error::BadParameter);
334        let result = participant.unset_listener().unwrap_err();
335        assert_eq!(result, crate::Error::BadParameter);
336        participant.inner = participant_id;
337    }
338}