cyclonedds/entity.rs
1//! The base of the DDS entity hierarchy.
2//!
3//! Most DDS objects ([`Participant`](crate::Participant),
4//! [`Topic`](crate::Topic), [`Reader`](crate::Reader),
5//! [`Writer`](crate::Writer), and others) are entities. See the
6//! [implementors of `Entity`](Entity#implementors) for the full list. This
7//! module provides the [`Entity`] trait with the common methods available to
8//! all entities.
9
10use crate::internal::ffi;
11use crate::{Result, Status};
12
13/// A unique opaque handle identifying an instance.
14///
15/// For keyed topics this corresponds to a specific key value, but applications
16/// should treat it as an opaque DDS handle.
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
18pub struct InstanceHandle {
19 pub(crate) inner: cyclonedds_sys::dds_instance_handle_t,
20}
21
22/// A raw entity ID for an entity.
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
24pub struct EntityId {
25 pub(crate) inner: cyclonedds_sys::dds_entity_t,
26}
27
28mod private {
29 /// Private trait for sealing downstream implementation of the
30 /// [`Entity`](super::Entity) trait.
31 pub trait Sealed {}
32}
33
34/// Common interface implemented by all members of the DDS entity hierarchy.
35///
36/// - [`Participant`](crate::Participant): the root entity representing membership in a domain.
37/// - [`WaitSet`](crate::WaitSet): blocks until one or more attached conditions are triggered.
38/// - [`GuardCondition`](crate::GuardCondition): a manually triggered condition for use with a
39/// [`WaitSet`](crate::WaitSet).
40/// - [`Topic<T>`](crate::Topic): names and types a data channel for a specific payload type `T`.
41/// - [`Publisher`](crate::Publisher): groups [`Writers`](crate::Writer) and controls their shared
42/// [`QoS`](crate::QoS).
43/// - [`Writer<T>`](crate::Writer): publishes samples of type `T` to a [`Topic`](crate::Topic).
44/// - [`Subscriber`](crate::Subscriber): groups [`Readers`](crate::Reader) and controls their
45/// shared [`QoS`](crate::QoS).
46/// - [`Reader<T>`](crate::Reader): receives samples of type `T` from a [`Topic`](crate::Topic).
47/// - [`ReadCondition<T>`](crate::ReadCondition): filters [`Reader`](crate::Reader) samples by
48/// [`sample`](crate::state::sample), [`view`](crate::state::view), and
49/// [`instance`](crate::state::instance) state.
50/// - [`QueryCondition<T, F>`](crate::QueryCondition): filters [`Reader`](crate::Reader)
51/// samples by [`sample state`](crate::State) and a predicate.
52pub trait Entity: private::Sealed {
53 /// Returns the [`EntityId`] of this entity.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// use cyclonedds::entity::Entity;
59 /// use cyclonedds::{Reader, Topic, Writer};
60 ///
61 /// # #[derive(
62 /// # cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
63 /// # )]
64 /// # struct Data {
65 /// # x: i32,
66 /// # }
67 /// # let domain = cyclonedds::Domain::default();
68 /// # let participant = cyclonedds::Participant::new(&domain)?;
69 /// let topic = Topic::<Data>::new(&participant, "Example")?;
70 /// let reader = Reader::new(&topic)?;
71 /// let writer = Writer::new(&topic)?;
72 ///
73 /// // The reader and the writer have distinct IDs.
74 /// assert_ne!(reader.id(), writer.id());
75 ///
76 /// # Ok::<_, cyclonedds::Error>(())
77 /// ```
78 fn id(&self) -> EntityId;
79
80 /// Returns the [`InstanceHandle`] of this entity.
81 ///
82 /// # Errors
83 ///
84 /// Returns an [`Error`](crate::Error) specifying the reason if the instance
85 /// handle fails to be retrieved.
86 ///
87 /// # Examples
88 ///
89 /// ```
90 /// use cyclonedds::entity::Entity;
91 /// use cyclonedds::{Reader, Topic, Writer};
92 ///
93 /// # #[derive(
94 /// # cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
95 /// # )]
96 /// # struct Data {
97 /// # x: i32,
98 /// # }
99 /// # let domain = cyclonedds::Domain::default();
100 /// # let participant = cyclonedds::Participant::new(&domain)?;
101 /// let topic = Topic::<Data>::new(&participant, "Example")?;
102 /// let reader = Reader::new(&topic)?;
103 /// let writer = Writer::new(&topic)?;
104 ///
105 /// // The reader and the writer have distinct instance handles.
106 /// assert_ne!(reader.instance_handle()?, writer.instance_handle()?);
107 ///
108 /// // Instance handles can be used to identify entities across various API
109 /// // calls. For example, the writer's handle appears in the set of matched
110 /// // publications.
111 /// let matched = reader.matched_publications()?;
112 /// assert_eq!(matched[0], writer.instance_handle()?);
113 /// # Ok::<_, cyclonedds::Error>(())
114 /// ```
115 fn instance_handle(&self) -> Result<InstanceHandle> {
116 let entity = self.id();
117 let inner = ffi::dds_get_instance_handle(entity.inner)?;
118 Ok(InstanceHandle { inner })
119 }
120
121 /// Returns the set of status flags that have changed since they were last
122 /// [`read`](crate::Reader::read) or [`taken`](crate::Reader::take).
123 ///
124 /// # Errors
125 ///
126 /// - Returns an [`Error`](crate::Error) if the status bits of the corresponding entity could
127 /// not be retrieved (e.g. the entity no longer exists).
128 ///
129 /// - Returns [`BadParameter`](crate::Error::BadParameter) if the retrieved bits do not
130 /// correspond to a valid [`Status`].
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use cyclonedds::entity::Entity;
136 /// use cyclonedds::{Reader, Status, Topic, Writer};
137 ///
138 /// # #[derive(
139 /// # cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
140 /// # )]
141 /// # struct Data {
142 /// # x: i32,
143 /// # }
144 /// # let domain = cyclonedds::Domain::default();
145 /// # let participant = cyclonedds::Participant::new(&domain)?;
146 /// let topic = Topic::<Data>::new(&participant, "Example")?;
147 /// let reader = Reader::new(&topic)?;
148 ///
149 /// // The reader has been created but nothing in particular has happened in
150 /// // terms of status changes.
151 /// let changed = reader.status_changes()?;
152 /// assert_eq!(changed, Status::empty());
153 ///
154 /// // The writer that is created will match with the reader.
155 /// let writer = Writer::new(&topic)?;
156 ///
157 /// // After a writer matches, the reader reports a status change.
158 /// let changed = reader.status_changes()?;
159 /// assert!(changed.contains(Status::SubscriptionMatched));
160 /// # Ok::<_, cyclonedds::Error>(())
161 /// ```
162 fn status_changes(&self) -> Result<Status> {
163 let entity = self.id();
164 let status = ffi::dds_get_status_changes(entity.inner)?;
165 Status::from_bits(status).ok_or(crate::error::Error::BadParameter)
166 }
167
168 /// Takes and clears the status flags matching `mask`, or all flags if
169 /// `mask` is `None`.
170 ///
171 /// Unlike [`read_status`](Entity::read_status), this clears the returned
172 /// flags on the entity.
173 ///
174 /// # Errors
175 ///
176 /// - Returns an [`Error`](crate::Error) if the status bits of the corresponding entity could
177 /// not be retrieved (e.g. the entity no longer exists or the status mask contains entries
178 /// that do not apply to the entity type).
179 ///
180 /// - Returns [`BadParameter`](crate::Error::BadParameter) if the retrieved bits do not
181 /// correspond to a valid [`Status`].
182 ///
183 /// # Examples
184 ///
185 /// ```
186 /// use cyclonedds::entity::Entity;
187 /// use cyclonedds::{Reader, Status, Topic, Writer};
188 ///
189 /// # #[derive(
190 /// # cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
191 /// # )]
192 /// # struct Data {
193 /// # x: i32,
194 /// # }
195 /// # let domain = cyclonedds::Domain::default();
196 /// # let participant = cyclonedds::Participant::new(&domain)?;
197 /// let topic = Topic::<Data>::new(&participant, "Example")?;
198 /// let reader = Reader::new(&topic)?;
199 /// let writer = Writer::new(&topic)?;
200 ///
201 /// // The reader has matched with the writer, so its status should have
202 /// // updated.
203 /// let status = reader.take_status(Some(Status::SubscriptionMatched))?;
204 /// assert!(status.contains(Status::SubscriptionMatched));
205 ///
206 /// // The flag has been cleared; a second take returns empty.
207 /// let cleared = reader.take_status(Some(Status::SubscriptionMatched))?;
208 /// assert!(cleared.is_empty());
209 /// # Ok::<_, cyclonedds::Error>(())
210 /// ```
211 fn take_status(&self, mask: Option<Status>) -> Result<Status> {
212 let entity = self.id();
213 let mask = mask.unwrap_or(Status::all()).bits();
214 let status = ffi::dds_take_status(entity.inner, mask)?;
215 Status::from_bits(status).ok_or(crate::error::Error::BadParameter)
216 }
217
218 /// Reads the status flags matching `mask` without clearing them, or all
219 /// flags if `mask` is `None`.
220 ///
221 /// # Errors
222 ///
223 /// - Returns an [`Error`](crate::Error) if the status bits of the corresponding entity could
224 /// not be retrieved (e.g. the entity no longer exists).
225 ///
226 /// - Returns [`BadParameter`](crate::Error::BadParameter) if the retrieved bits do not
227 /// correspond to a valid [`Status`].
228 ///
229 /// # Examples
230 ///
231 /// ```
232 /// use cyclonedds::entity::Entity;
233 /// use cyclonedds::{Reader, Status, Topic, Writer};
234 ///
235 /// # #[derive(
236 /// # cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
237 /// # )]
238 /// # struct Data {
239 /// # x: i32,
240 /// # }
241 /// # let domain = cyclonedds::Domain::default();
242 /// # let participant = cyclonedds::Participant::new(&domain)?;
243 /// let topic = Topic::<Data>::new(&participant, "Example")?;
244 /// let reader = Reader::new(&topic)?;
245 /// let writer = Writer::new(&topic)?;
246 ///
247 /// // The reader has matched with the writer, so its status should have
248 /// // updated.
249 /// let status = reader.read_status(Some(Status::SubscriptionMatched))?;
250 /// assert!(status.contains(Status::SubscriptionMatched));
251 ///
252 /// // The flag is preserved; a second read returns the same value.
253 /// let same = reader.read_status(Some(Status::SubscriptionMatched))?;
254 /// assert_eq!(status, same);
255 /// # Ok::<_, cyclonedds::Error>(())
256 /// ```
257 fn read_status(&self, mask: Option<Status>) -> Result<Status> {
258 let entity = self.id();
259 let mask = mask.unwrap_or(Status::all()).bits();
260 let status = ffi::dds_read_status(entity.inner, mask)?;
261 Status::from_bits(status).ok_or(crate::error::Error::BadParameter)
262 }
263
264 /// Returns the status mask enabled on the entity.
265 ///
266 /// # Errors
267 ///
268 /// - Returns an [`Error`](crate::Error) if the status mask of the corresponding entity could
269 /// not be retrieved (e.g. the entity no longer exists).
270 ///
271 /// - Returns [`BadParameter`](crate::Error::BadParameter) if the retrieved bits do not
272 /// correspond to a valid [`Status`].
273 ///
274 /// # Examples
275 /// ```
276 /// use cyclonedds::entity::Entity;
277 /// use cyclonedds::{Status, Topic, Writer};
278 ///
279 /// # #[derive(
280 /// # cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
281 /// # )]
282 /// # struct Data {
283 /// # x: i32,
284 /// # }
285 /// # let domain = cyclonedds::Domain::default();
286 /// # let participant = cyclonedds::Participant::new(&domain)?;
287 /// let topic = Topic::<Data>::new(&participant, "Example")?;
288 /// let writer = Writer::new(&topic)?;
289 ///
290 /// // Get the initial active status mask.
291 /// assert_eq!(
292 /// writer.status_mask()?,
293 /// Status::OfferedDeadlineMissed
294 /// | Status::OfferedIncompatibleQoS
295 /// | Status::LivelinessLost
296 /// | Status::PublicationMatched
297 /// );
298 /// # Ok::<_, cyclonedds::Error>(())
299 /// ```
300 fn status_mask(&self) -> Result<Status> {
301 let entity = self.id();
302 let mask = ffi::dds_get_status_mask(entity.inner)?;
303 Status::from_bits(mask).ok_or(crate::error::Error::BadParameter)
304 }
305
306 /// Sets and enables a status mask on the entity.
307 ///
308 /// Only status flags included in `mask` will trigger listener callbacks or
309 /// be reported via [`status_changes`](Entity::status_changes).
310 ///
311 /// # Errors
312 ///
313 /// - Returns an [`Error`](crate::Error) if the status mask of the corresponding entity could
314 /// not be set (e.g. the entity no longer exists).
315 ///
316 /// # Examples
317 /// ```
318 /// use cyclonedds::entity::Entity;
319 /// use cyclonedds::{Status, Topic, Writer};
320 ///
321 /// # #[derive(
322 /// # cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
323 /// # )]
324 /// # struct Data {
325 /// # x: i32,
326 /// # }
327 /// # let domain = cyclonedds::Domain::default();
328 /// # let participant = cyclonedds::Participant::new(&domain)?;
329 /// let topic = Topic::<Data>::new(&participant, "Example")?;
330 /// let writer = Writer::new(&topic)?;
331 ///
332 /// // Set the active status mask.
333 /// writer.set_status_mask(Status::PublicationMatched)?;
334 /// // Get the active status mask.
335 /// assert_eq!(writer.status_mask()?, Status::PublicationMatched);
336 /// # Ok::<_, cyclonedds::Error>(())
337 /// ```
338 fn set_status_mask(&self, mask: Status) -> Result<()> {
339 let entity = self.id();
340 let mask = mask.bits();
341 ffi::dds_set_status_mask(entity.inner, mask)
342 }
343}
344
345macro_rules! impl_entity {
346 ($ty:ty) => {
347 impl private::Sealed for $ty {}
348
349 impl Entity for $ty {
350 fn id(&self) -> EntityId {
351 EntityId { inner: self.inner }
352 }
353 }
354 };
355 ($ty:ty where $($bounds:tt)*) => {
356 impl<$($bounds)*> private::Sealed for $ty {}
357
358 impl<$($bounds)*> Entity for $ty {
359 fn id(&self) -> EntityId {
360 EntityId { inner: self.inner }
361 }
362 }
363 };
364}
365
366impl_entity!(crate::Participant<'_>);
367impl_entity!(crate::Topic<'_, '_, T> where T: crate::Topicable);
368impl_entity!(crate::Publisher<'_, '_>);
369impl_entity!(crate::Subscriber<'_, '_>);
370impl_entity!(crate::Reader<'_, '_, '_, T> where T: crate::Topicable);
371impl_entity!(crate::Writer<'_, '_, '_, T> where T: crate::Topicable);
372impl_entity!(crate::ReadCondition<'_, '_, '_, '_, T> where T: crate::Topicable);
373impl_entity!(crate::QueryCondition<'_, '_, '_, '_, T, F> where T: crate::Topicable, F: Fn(&T) -> bool);
374impl_entity!(crate::GuardCondition<'_>);
375impl_entity!(crate::WaitSet<'_, '_, '_, A> where A);
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn test_entity_id_all_entity_types() {
383 let domain_id = crate::tests::domain::unique_id();
384 let domain = crate::Domain::new(domain_id).unwrap();
385 let participant = crate::Participant::new(&domain).unwrap();
386 let topic_name = crate::tests::topic::unique_name();
387 let topic =
388 crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
389 let publisher = crate::Publisher::new(&participant).unwrap();
390 let subscriber = crate::Subscriber::new(&participant).unwrap();
391 let reader = crate::Reader::new(&topic).unwrap();
392 let writer = crate::Writer::new(&topic).unwrap();
393 let read_condition = crate::ReadCondition::new(&reader, crate::state::sample::Any).unwrap();
394 let query_condition =
395 crate::QueryCondition::new(&reader, crate::State::empty(), |_| true).unwrap();
396 let guard_condition = crate::GuardCondition::new(&participant).unwrap();
397 let waitset = crate::WaitSet::<()>::new(&participant).unwrap();
398
399 assert_eq!(participant.id().inner, participant.inner);
400 assert_eq!(topic.id().inner, topic.inner);
401 assert_eq!(publisher.id().inner, publisher.inner);
402 assert_eq!(subscriber.id().inner, subscriber.inner);
403 assert_eq!(reader.id().inner, reader.inner);
404 assert_eq!(writer.id().inner, writer.inner);
405 assert_eq!(read_condition.id().inner, read_condition.inner);
406 assert_eq!(query_condition.id().inner, query_condition.inner);
407 assert_eq!(guard_condition.id().inner, guard_condition.inner);
408 assert_eq!(waitset.id().inner, waitset.inner);
409 }
410
411 #[test]
412 fn test_entity_methods_on_invalid_participant() {
413 let domain_id = crate::tests::domain::unique_id();
414 let domain = crate::Domain::new(domain_id).unwrap();
415 let mut participant = crate::Participant::new(&domain).unwrap();
416 let participant_id = participant.inner;
417 participant.inner = 0;
418
419 assert_eq!(
420 crate::Error::BadParameter,
421 participant.instance_handle().unwrap_err()
422 );
423 assert_eq!(
424 crate::Error::BadParameter,
425 participant.status_changes().unwrap_err()
426 );
427 assert_eq!(
428 crate::Error::BadParameter,
429 participant.take_status(None).unwrap_err()
430 );
431 assert_eq!(
432 crate::Error::BadParameter,
433 participant.read_status(None).unwrap_err()
434 );
435 assert_eq!(
436 crate::Error::BadParameter,
437 participant.status_mask().unwrap_err()
438 );
439 assert_eq!(
440 crate::Error::BadParameter,
441 participant
442 .set_status_mask(crate::Status::InconsistentTopic)
443 .unwrap_err()
444 );
445
446 participant.inner = participant_id;
447 }
448
449 #[test]
450 fn test_entity_methods_on_participant() {
451 let domain_id = crate::tests::domain::unique_id();
452 let domain = crate::Domain::new(domain_id).unwrap();
453 let participant = crate::Participant::new(&domain).unwrap();
454
455 let result = participant.instance_handle();
456 assert!(result.is_ok());
457 let status_changes = participant.status_changes().unwrap();
458 assert!(status_changes.is_empty());
459 let result = participant.set_status_mask(crate::Status::empty());
460 assert!(result.is_ok());
461 let mask = participant.status_mask().unwrap();
462 assert_eq!(mask, crate::Status::empty());
463 let status = participant
464 .read_status(Some(crate::Status::empty()))
465 .unwrap();
466 assert!(status.is_empty());
467 let status = participant
468 .take_status(Some(crate::Status::empty()))
469 .unwrap();
470 assert!(status.is_empty());
471 }
472
473 #[test]
474 fn test_entity_methods_on_reader() {
475 let domain_id = crate::tests::domain::unique_id();
476 let domain = crate::Domain::new(domain_id).unwrap();
477 let topic_name = crate::tests::topic::unique_name();
478 let participant = crate::Participant::new(&domain).unwrap();
479 let topic =
480 crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
481 let reader = crate::Reader::new(&topic).unwrap();
482
483 let result = reader.instance_handle();
484 assert!(result.is_ok());
485 let status_changes = reader.status_changes().unwrap();
486 assert!(status_changes.is_empty());
487 let result = reader.set_status_mask(crate::Status::SubscriptionMatched);
488 assert!(result.is_ok());
489 let mask = reader.status_mask().unwrap();
490 assert_eq!(mask, crate::Status::SubscriptionMatched);
491 let status = reader
492 .read_status(Some(crate::Status::SubscriptionMatched))
493 .unwrap();
494 assert!(status.is_empty());
495 let status = reader
496 .take_status(Some(crate::Status::SubscriptionMatched))
497 .unwrap();
498 assert!(status.is_empty());
499 }
500}