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}