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}