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}