Skip to main content

cyclonedds/
read_condition.rs

1use crate::internal::ffi;
2use crate::{Reader, Result, State};
3
4/// A filter on a [`Reader`](crate::Reader) that restricts samples by their
5/// [`State`](crate::State).
6///
7/// A `ReadCondition` is created against a reader with a state mask and can be
8/// attached to a [`WaitSet`](crate::WaitSet) to trigger when matching samples
9/// become available. Reading via the condition returns only samples whose
10/// combined sample, view, and instance state matches the mask.
11///
12/// # Examples
13///
14/// ```no_run
15/// use cyclonedds::{Duration, ReadCondition, WaitSet, state};
16/// # use cyclonedds::{Domain, Participant, Topic, Reader};
17/// # let domain = Domain::default();
18/// # let participant = Participant::new(&domain)?;
19/// # #[derive(
20/// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
21/// # )]
22/// # struct Data {
23/// #     x: i32,
24/// #     y: i32,
25/// # }
26///
27/// let topic = Topic::<Data>::new(&participant, "MyTopic")?;
28/// let reader = Reader::new(&topic)?;
29///
30/// let condition = ReadCondition::new(
31///     &reader,
32///     state::sample::Fresh | state::instance::Any | state::view::Any,
33/// )?;
34/// let mut waitset = WaitSet::<()>::new(&participant)?;
35/// waitset.attach(&condition, None)?;
36/// waitset.wait(Duration::INFINITE)?;
37///
38/// let samples = condition.take()?;
39/// # Ok::<_, cyclonedds::Error>(())
40/// ```
41#[derive(Debug)]
42pub struct ReadCondition<'domain, 'participant, 'topic, 'reader, T>
43where
44    T: crate::Topicable,
45{
46    pub(crate) inner: cyclonedds_sys::dds_entity_t,
47    phantom: std::marker::PhantomData<&'reader Reader<'domain, 'participant, 'topic, T>>,
48}
49
50impl<'d, 'p, 't, 'r, T> ReadCondition<'d, 'p, 't, 'r, T>
51where
52    T: crate::Topicable,
53{
54    /// Creates a new [`ReadCondition`] on `reader` that matches samples whose
55    /// state satisfies `mask`.
56    ///
57    /// # Errors
58    ///
59    /// Returns an [`Error`](crate::Error) if the read condition fails to
60    /// create.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use cyclonedds::{ReadCondition, state};
66    /// # use cyclonedds::{Domain, Participant, Topic, Reader};
67    /// # let domain = Domain::default();
68    /// # let participant = Participant::new(&domain)?;
69    /// # #[derive(
70    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
71    /// # )]
72    /// # struct Data {
73    /// #     x: i32,
74    /// #     y: i32,
75    /// # }
76    ///
77    /// let topic = Topic::<Data>::new(&participant, "MyTopic")?;
78    /// let reader = Reader::new(&topic)?;
79    /// let condition = ReadCondition::new(&reader, state::sample::Fresh)?;
80    /// # Ok::<_, cyclonedds::Error>(())
81    /// ```
82    pub fn new(reader: &'r Reader<'d, 'p, 't, T>, mask: State) -> Result<Self> {
83        let inner = ffi::dds_create_readcondition(reader.inner, mask.bits())?;
84        Ok(Self {
85            inner,
86            phantom: std::marker::PhantomData,
87        })
88    }
89
90    /// Returns the state mask this condition was created with.
91    ///
92    /// # Errors
93    ///
94    /// Returns an [`Error`](crate::Error) if the mask returned by the read
95    /// condition is invalid.
96    ///
97    /// # Examples
98    ///
99    /// ```
100    /// use cyclonedds::{ReadCondition, state};
101    /// # use cyclonedds::{Domain, Participant, Topic, Reader};
102    /// # let domain = Domain::default();
103    /// # let participant = Participant::new(&domain)?;
104    /// # #[derive(
105    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
106    /// # )]
107    /// # struct Data {
108    /// #     x: i32,
109    /// #     y: i32,
110    /// # }
111    ///
112    /// let topic = Topic::<Data>::new(&participant, "MyTopic")?;
113    /// let reader = Reader::new(&topic)?;
114    /// let condition = ReadCondition::new(&reader, state::sample::Fresh)?;
115    /// assert_eq!(condition.mask()?, state::sample::Fresh);
116    /// # Ok::<_, cyclonedds::Error>(())
117    /// ```
118    pub fn mask(&self) -> Result<State> {
119        let mask = ffi::dds_get_mask(self.inner)?;
120        crate::state::State::from_bits(mask).ok_or(crate::error::Error::NonSpecific)
121    }
122
123    /// Returns `true` if this condition is currently triggered.
124    ///
125    /// A condition is triggered when samples matching its mask are available
126    /// in the reader cache.
127    ///
128    /// # Errors
129    ///
130    /// Returns an [`Error`](crate::Error) if the read condition fails to read
131    /// the trigger state.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use cyclonedds::{ReadCondition, state};
137    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
138    /// # let domain = Domain::default();
139    /// # let participant = Participant::new(&domain)?;
140    /// # #[derive(
141    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
142    /// # )]
143    /// # struct Data {
144    /// #     x: i32,
145    /// #     y: i32,
146    /// # }
147    ///
148    /// let topic = Topic::<Data>::new(&participant, "MyTopic")?;
149    /// let reader = Reader::new(&topic)?;
150    /// let writer = Writer::new(&topic)?;
151    ///
152    /// let condition = ReadCondition::new(&reader, state::sample::Fresh)?;
153    /// writer.write(&Data::default())?;
154    /// assert!(condition.triggered()?);
155    /// Ok::<_, cyclonedds::Error>(())
156    /// ```
157    pub fn triggered(&self) -> Result<bool> {
158        ffi::dds_triggered(self.inner)
159    }
160
161    /// Removes and returns all samples matching this condition's mask from the
162    /// reader cache.
163    ///
164    /// # Errors
165    ///
166    /// Returns an [`Error`](crate::Error) if the read condition fails to take
167    /// samples.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use cyclonedds::{ReadCondition, state};
173    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
174    /// # let domain = Domain::default();
175    /// # let participant = Participant::new(&domain)?;
176    /// # #[derive(
177    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
178    /// # )]
179    /// # struct Data {
180    /// #     x: i32,
181    /// #     y: i32,
182    /// # }
183    ///
184    /// let topic = Topic::<Data>::new(&participant, "MyTopic")?;
185    /// let reader = Reader::new(&topic)?;
186    /// let writer = Writer::new(&topic)?;
187    ///
188    /// let condition = ReadCondition::new(
189    ///     &reader,
190    ///     state::sample::Stale | state::instance::Any | state::view::Any,
191    /// )?;
192    /// writer.write(&Data::default())?;
193    ///
194    /// // No sample matches this state initially.
195    /// let samples = condition.take()?;
196    /// assert_eq!(samples.len(), 0);
197    ///
198    /// // Attempt a normal read.
199    /// assert_eq!(reader.read()?.len(), 1);
200    ///
201    /// // Sample should now match this state because they're stale.
202    /// let samples = condition.take()?;
203    /// assert_eq!(samples.len(), 1);
204    ///
205    /// // Samples should be removed from the cache.
206    /// assert_eq!(condition.take()?.len(), 0);
207    /// # Ok::<_, cyclonedds::Error>(())
208    /// ```
209    pub fn take(&self) -> Result<Vec<crate::sample::SampleOrKey<T>>>
210    where
211        T: std::clone::Clone,
212    {
213        ffi::dds_take(self.inner)
214    }
215
216    /// Returns all samples matching this condition's mask without removing
217    /// them from the reader cache.
218    ///
219    /// # Errors
220    ///
221    /// Returns an [`Error`](crate::Error) if the read condition fails to read
222    /// samples.
223    ///
224    /// # Examples
225    ///
226    /// ```
227    /// use cyclonedds::{ReadCondition, state};
228    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
229    /// # let domain = Domain::default();
230    /// # let participant = Participant::new(&domain)?;
231    /// # #[derive(
232    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
233    /// # )]
234    /// # struct Data {
235    /// #     x: i32,
236    /// #     y: i32,
237    /// # }
238    ///
239    /// let topic = Topic::<Data>::new(&participant, "MyTopic")?;
240    /// let reader = Reader::new(&topic)?;
241    /// let writer = Writer::new(&topic)?;
242    ///
243    /// let condition = ReadCondition::new(
244    ///     &reader,
245    ///     state::sample::Stale | state::instance::Any | state::view::Any,
246    /// )?;
247    /// writer.write(&Data::default())?;
248    ///
249    /// // No sample matches this state initially.
250    /// let samples = condition.read()?;
251    /// assert_eq!(samples.len(), 0);
252    ///
253    /// // Attempt a normal read.
254    /// assert_eq!(reader.read()?.len(), 1);
255    ///
256    /// // Sample should now match this state because they're stale.
257    /// let samples = condition.read()?;
258    /// assert_eq!(samples.len(), 1);
259    ///
260    /// // Samples remain in the cache.
261    /// assert_eq!(condition.read()?.len(), 1);
262    /// # Ok::<_, cyclonedds::Error>(())
263    /// ```
264    pub fn read(&self) -> Result<Vec<crate::sample::SampleOrKey<T>>>
265    where
266        T: std::clone::Clone,
267    {
268        ffi::dds_read(self.inner)
269    }
270
271    /// Returns all samples matching this condition's mask without marking them
272    /// as read or removing them from the cache.
273    ///
274    /// # Errors
275    ///
276    /// Returns an [`Error`](crate::Error) if the read condition fails to peek
277    /// samples.
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// use cyclonedds::{ReadCondition, state};
283    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
284    /// # let domain = Domain::default();
285    /// # let participant = Participant::new(&domain)?;
286    /// # #[derive(
287    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
288    /// # )]
289    /// # struct Data {
290    /// #     x: i32,
291    /// #     y: i32,
292    /// # }
293    ///
294    /// let topic = Topic::<Data>::new(&participant, "MyTopic")?;
295    /// let reader = Reader::new(&topic)?;
296    /// let writer = Writer::new(&topic)?;
297    ///
298    /// let condition = ReadCondition::new(
299    ///     &reader,
300    ///     state::sample::Stale | state::instance::Any | state::view::Any,
301    /// )?;
302    /// writer.write(&Data::default())?;
303    ///
304    /// // No sample matches this state initially.
305    /// let samples = condition.peek()?;
306    /// assert_eq!(samples.len(), 0);
307    ///
308    /// // Attempt a normal read.
309    /// assert_eq!(reader.read()?.len(), 1);
310    ///
311    /// // Sample should now match this state because they're stale.
312    /// let samples = condition.peek()?;
313    /// assert_eq!(samples.len(), 1);
314    ///
315    /// // Samples remain in the cache.
316    /// assert_eq!(condition.peek()?.len(), 1);
317    /// # Ok::<_, cyclonedds::Error>(())
318    /// ```
319    pub fn peek(&self) -> Result<Vec<crate::sample::SampleOrKey<T>>>
320    where
321        T: std::clone::Clone,
322    {
323        ffi::dds_peek(self.inner)
324    }
325}
326
327impl<T> Drop for ReadCondition<'_, '_, '_, '_, T>
328where
329    T: crate::Topicable,
330{
331    fn drop(&mut self) {
332        let result = ffi::dds_delete(self.inner);
333        debug_assert!(
334            result.is_ok(),
335            "unable to delete {self:?}: failed with {result:?}"
336        );
337    }
338}
339
340#[cfg(test)]
341mod tests {
342    use super::*;
343    use crate::state;
344
345    #[test]
346    fn test_read_condition_create() {
347        let domain_id = crate::tests::domain::unique_id();
348        let domain = crate::Domain::new(domain_id).unwrap();
349        let topic_name = crate::tests::topic::unique_name();
350        let participant = crate::Participant::new(&domain).unwrap();
351        let topic =
352            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
353        let reader = crate::Reader::new(&topic).unwrap();
354        let _ = ReadCondition::new(
355            &reader,
356            state::sample::Any | state::instance::Any | state::view::Any,
357        )
358        .unwrap();
359    }
360
361    #[test]
362    fn test_read_condition_create_with_invalid_reader() {
363        let domain_id = crate::tests::domain::unique_id();
364        let domain = crate::Domain::new(domain_id).unwrap();
365        let topic_name = crate::tests::topic::unique_name();
366        let participant = crate::Participant::new(&domain).unwrap();
367        let topic =
368            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
369        let mut reader = crate::Reader::new(&topic).unwrap();
370        let reader_id = reader.inner;
371        reader.inner = 0;
372        let result = ReadCondition::new(
373            &reader,
374            state::sample::Any | state::instance::Any | state::view::Any,
375        )
376        .unwrap_err();
377        reader.inner = reader_id;
378        assert_eq!(result, crate::Error::BadParameter);
379    }
380
381    #[test]
382    fn test_read_condition_get_mask() {
383        let domain_id = crate::tests::domain::unique_id();
384        let domain = crate::Domain::new(domain_id).unwrap();
385        let topic_name = crate::tests::topic::unique_name();
386        let participant = crate::Participant::new(&domain).unwrap();
387        let topic =
388            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
389        let reader = crate::Reader::new(&topic).unwrap();
390
391        let mask = state::sample::Any | state::instance::Any | state::view::Any;
392
393        let read_condition = ReadCondition::new(&reader, mask).unwrap();
394        let result = read_condition.mask().unwrap();
395        assert_eq!(result, mask);
396
397        let mask = state::sample::Fresh | state::instance::Unregistered | state::view::Old;
398        let result = read_condition.mask().unwrap();
399        assert_ne!(result, mask);
400
401        let read_condition = ReadCondition::new(&reader, mask).unwrap();
402        let result = read_condition.mask().unwrap();
403        assert_eq!(result, mask);
404    }
405
406    #[test]
407    fn test_read_condition_get_mask_on_invalid_read_condition() {
408        let domain_id = crate::tests::domain::unique_id();
409        let domain = crate::Domain::new(domain_id).unwrap();
410        let topic_name = crate::tests::topic::unique_name();
411        let participant = crate::Participant::new(&domain).unwrap();
412        let topic =
413            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
414        let reader = crate::Reader::new(&topic).unwrap();
415        let mut read_condition = ReadCondition::new(
416            &reader,
417            state::sample::Any | state::instance::Any | state::view::Any,
418        )
419        .unwrap();
420        let read_condition_id = read_condition.inner;
421        read_condition.inner = 0;
422        let result = read_condition.mask().unwrap_err();
423        assert_eq!(result, crate::Error::BadParameter);
424        let result = read_condition.triggered().unwrap_err();
425        assert_eq!(result, crate::Error::BadParameter);
426        read_condition.inner = read_condition_id;
427    }
428
429    #[test]
430    fn test_read_condition_triggering_reads() {
431        let domain_id = crate::tests::domain::unique_id();
432        let domain = crate::Domain::new(domain_id).unwrap();
433        let topic_name = crate::tests::topic::unique_name();
434        let participant = crate::Participant::new(&domain).unwrap();
435        let topic =
436            crate::Topic::<crate::tests::topic::Data>::new(&participant, &topic_name).unwrap();
437        let reader = crate::Reader::new(&topic).unwrap();
438        let writer = crate::Writer::new(&topic).unwrap();
439
440        let mask = state::sample::Stale | state::instance::Any | state::view::Any;
441
442        let read_condition = ReadCondition::new(&reader, mask).unwrap();
443
444        let sample = crate::tests::topic::Data {
445            x: 101,
446            y: 202,
447            message: "hello".to_string(),
448        };
449        writer.write(&sample).unwrap();
450
451        let read_condition_received = read_condition.read().unwrap();
452        assert_eq!(read_condition_received.len(), 0);
453        let triggered = read_condition.triggered().unwrap();
454        assert!(!triggered);
455
456        let reader_received = reader.read().unwrap();
457        assert_eq!(reader_received.len(), 1);
458        assert_eq!(*reader_received[0], sample);
459        assert_eq!(
460            reader_received[0].info().state,
461            state::sample::Fresh | state::view::New | state::instance::Alive
462        );
463
464        let triggered = read_condition.triggered().unwrap();
465        assert!(triggered);
466
467        let read_condition_received = read_condition.peek().unwrap();
468        assert_eq!(read_condition_received.len(), 1);
469        assert_eq!(*read_condition_received[0], sample);
470
471        let triggered = read_condition.triggered().unwrap();
472        assert!(triggered);
473
474        let read_condition_received = read_condition.take().unwrap();
475        assert_eq!(read_condition_received.len(), 1);
476        assert_eq!(*read_condition_received[0], sample);
477
478        let triggered = read_condition.triggered().unwrap();
479        assert!(!triggered);
480
481        let reader_received = reader.read().unwrap();
482        assert!(reader_received.is_empty());
483
484        let read_condition_received = read_condition.read().unwrap();
485        assert!(read_condition_received.is_empty());
486    }
487}