Skip to main content

cyclonedds/
sample.rs

1//! Types representing received DDS samples and their associated metadata.
2//!
3//! A received sample is either a full data sample of type
4//! [`T`](crate::Topicable) or a key-only sample carrying the instance key
5//! [`T::Key`](crate::Topicable::Key).
6//!
7//! This distinction arises in DDS when an instance is disposed or unregistered,
8//! the reader receives a notification carrying only the key rather than a full
9//! payload.
10//!
11//! Samples are obtained via [`Reader::peek`](crate::Reader::peek),
12//! [`Reader::read`](crate::Reader::read), and
13//! [`Reader::take`](crate::Reader::take), or their equivalents on
14//! [`ReadCondition`](crate::ReadCondition) and
15//! [`QueryCondition`](crate::QueryCondition).
16
17use crate::Topicable;
18
19#[derive(Clone, Debug)]
20pub(crate) enum SampleOrKeyInner<T>
21where
22    T: crate::Topicable,
23{
24    Sample {
25        sample: Box<T>,
26        materialized_key: std::cell::OnceCell<Box<T::Key>>,
27    },
28    Key {
29        key: Box<T::Key>,
30        materialized_sample: std::cell::OnceCell<Box<T>>,
31    },
32}
33
34impl<T> SampleOrKeyInner<T>
35where
36    T: crate::Topicable,
37{
38    pub fn new_sample(sample: T) -> Self {
39        Self::Sample {
40            sample: Box::new(sample),
41            materialized_key: std::cell::OnceCell::new(),
42        }
43    }
44
45    pub fn new_key(key: T::Key) -> Self {
46        Self::Key {
47            key: Box::new(key),
48            materialized_sample: std::cell::OnceCell::new(),
49        }
50    }
51
52    pub fn key(&self) -> &T::Key {
53        match self {
54            Self::Sample {
55                sample,
56                materialized_key,
57            } => materialized_key.get_or_init(|| Box::new(sample.as_key())),
58            Self::Key { key, .. } => key,
59        }
60    }
61
62    pub fn sample(&self) -> &T {
63        match self {
64            Self::Sample { sample, .. } => sample,
65            Self::Key {
66                key,
67                materialized_sample,
68            } => materialized_sample.get_or_init(|| Box::new(T::from_key(key))),
69        }
70    }
71}
72
73/// A received sample, which is either a full payload of type
74/// [`T`](crate::Topicable) or a key-only payload carrying
75/// [`T::Key`](crate::Topicable::Key).
76///
77/// Key-only samples are produced when an instance is disposed or unregistered
78/// by a writer. [`SampleOrKey`] derefs to `T` in both cases: for key-only
79/// samples this materializes a default `T` from the key via
80/// [`Topicable::from_key`].
81///
82/// Use [`view`](SampleOrKey::view) to distinguish between the two cases without
83/// triggering materialisation.
84pub struct SampleOrKey<T>
85where
86    T: crate::Topicable,
87{
88    inner: SampleOrKeyInner<T>,
89    pub(crate) info: Info,
90}
91
92impl<T> std::clone::Clone for SampleOrKey<T>
93where
94    T: Topicable + std::clone::Clone,
95    T::Key: std::clone::Clone,
96{
97    fn clone(&self) -> Self {
98        Self {
99            inner: self.inner.clone(),
100            info: self.info,
101        }
102    }
103}
104
105impl<T> std::fmt::Debug for SampleOrKey<T>
106where
107    T: Topicable + std::fmt::Debug,
108    T::Key: std::fmt::Debug,
109{
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        let mut f = f.debug_struct("SampleOrKey");
112
113        let f = match &self.inner {
114            SampleOrKeyInner::Sample { sample, .. } => f.field("sample", sample),
115            SampleOrKeyInner::Key { key, .. } => f.field("key", key),
116        };
117
118        f.field("info", &self.info).finish()
119    }
120}
121
122impl<T> SampleOrKey<T>
123where
124    T: crate::Topicable,
125{
126    /// Create a new sample or key provided a full sample and sample info.
127    pub(crate) fn new_sample(sample: T, info: Info) -> Self {
128        let inner = SampleOrKeyInner::new_sample(sample);
129        Self { inner, info }
130    }
131
132    /// Create a new sample or key provided a key and sample info.
133    pub(crate) fn new_key(key: T::Key, info: Info) -> Self {
134        let inner = SampleOrKeyInner::new_key(key);
135        Self { inner, info }
136    }
137
138    /// Returns the metadata associated with this sample.
139    ///
140    /// # Examples
141    ///
142    /// ```
143    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
144    /// # let domain = Domain::default();
145    /// # let participant = Participant::new(&domain)?;
146    /// # #[derive(
147    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
148    /// # )]
149    /// # struct Data {
150    /// #     x: i32,
151    /// # }
152    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
153    /// # let reader = Reader::new(&topic)?;
154    /// # let writer = Writer::new(&topic)?;
155    /// # writer.write(&Data::default())?;
156    /// let sample = &reader.take()?[0];
157    /// let info = sample.info();
158    /// println!("source timestamp: {:?}", info.source_timestamp);
159    /// # Ok::<_, cyclonedds::Error>(())
160    /// ```
161    pub const fn info(&self) -> &Info {
162        &self.info
163    }
164
165    /// Returns a reference to the full sample payload, or `None` if this is a
166    /// key-only sample.
167    ///
168    /// # Examples
169    ///
170    /// ```
171    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
172    /// # let domain = Domain::default();
173    /// # let participant = Participant::new(&domain)?;
174    /// # #[derive(
175    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
176    /// # )]
177    /// # struct Data {
178    /// #     x: i32,
179    /// # }
180    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
181    /// # let reader = Reader::new(&topic)?;
182    /// # let writer = Writer::new(&topic)?;
183    /// # writer.write(&Data::default())?;
184    /// let sample = &reader.take()?[0];
185    /// if let Some(data) = sample.sample() {
186    ///     println!("payload: {data:?}");
187    /// }
188    /// # Ok::<_, cyclonedds::Error>(())
189    /// ```
190    pub fn sample(&self) -> Option<&T> {
191        match &self.inner {
192            SampleOrKeyInner::Sample { sample, .. } => Some(sample),
193            SampleOrKeyInner::Key { .. } => None,
194        }
195    }
196
197    /// Consumes `self` and returns the full sample payload, or `None` if this
198    /// is a key-only sample.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
204    /// # let domain = Domain::default();
205    /// # let participant = Participant::new(&domain)?;
206    /// # #[derive(
207    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
208    /// # )]
209    /// # struct Data {
210    /// #     x: i32,
211    /// # }
212    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
213    /// # let reader = Reader::new(&topic)?;
214    /// # let writer = Writer::new(&topic)?;
215    /// # writer.write(&Data::default())?;
216    /// let sample = reader.take()?.into_iter().next().unwrap();
217    /// if let Some(data) = sample.into_sample() {
218    ///     println!("payload: {data:?}");
219    /// }
220    /// # Ok::<_, cyclonedds::Error>(())
221    /// ```
222    pub fn into_sample(self) -> Option<T> {
223        match self.inner {
224            SampleOrKeyInner::Sample { sample, .. } => Some(*sample),
225            SampleOrKeyInner::Key { .. } => None,
226        }
227    }
228
229    /// Returns `true` if this is a full sample.
230    ///
231    /// # Examples
232    ///
233    /// ```
234    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
235    /// # let domain = Domain::default();
236    /// # let participant = Participant::new(&domain)?;
237    /// # #[derive(
238    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
239    /// # )]
240    /// # struct Data {
241    /// #     x: i32,
242    /// # }
243    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
244    /// # let reader = Reader::new(&topic)?;
245    /// # let writer = Writer::new(&topic)?;
246    /// # writer.write(&Data::default())?;
247    /// let sample = reader.take()?.into_iter().next().unwrap();
248    /// assert!(sample.is_sample());
249    /// # Ok::<_, cyclonedds::Error>(())
250    /// ```
251    pub const fn is_sample(&self) -> bool {
252        matches!(self.inner, SampleOrKeyInner::Sample { .. })
253    }
254
255    /// Returns `true` if this is a full sample and `f` returns `true` for its
256    /// payload.
257    ///
258    /// # Examples
259    ///
260    /// ```
261    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
262    /// # let domain = Domain::default();
263    /// # let participant = Participant::new(&domain)?;
264    /// # #[derive(
265    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
266    /// # )]
267    /// # struct Data {
268    /// #     #[dds(key)]
269    /// #     x: i32,
270    /// # }
271    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
272    /// # let reader = Reader::new(&topic)?;
273    /// # let writer = Writer::new(&topic)?;
274    /// # writer.write(&Data::default())?;
275    /// let sample = reader.take()?.into_iter().next().unwrap();
276    /// assert!(sample.is_sample_and(|data| data.x == 0));
277    /// # Ok::<_, cyclonedds::Error>(())
278    /// ```
279    pub fn is_sample_and(&self, f: impl FnOnce(&T) -> bool) -> bool {
280        match &self.inner {
281            SampleOrKeyInner::Sample { sample, .. } => f(sample),
282            SampleOrKeyInner::Key { .. } => false,
283        }
284    }
285
286    /// Returns a reference to the instance key, or `None` if this is a full
287    /// sample.
288    ///
289    /// # Examples
290    ///
291    /// ```
292    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
293    /// # let domain = Domain::default();
294    /// # let participant = Participant::new(&domain)?;
295    /// # #[derive(
296    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
297    /// # )]
298    /// # struct Data {
299    /// #     x: i32,
300    /// # }
301    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
302    /// # let reader = Reader::new(&topic)?;
303    /// # let writer = Writer::new(&topic)?;
304    /// # writer.write(&Data::default())?;
305    /// let sample = reader.take()?.into_iter().next().unwrap();
306    /// if let Some(key) = sample.key() {
307    ///     println!("key-only notification: {key:?}");
308    /// }
309    /// # Ok::<_, cyclonedds::Error>(())
310    /// ```
311    pub fn key(&self) -> Option<&T::Key> {
312        match &self.inner {
313            SampleOrKeyInner::Sample { .. } => None,
314            SampleOrKeyInner::Key { key, .. } => Some(key),
315        }
316    }
317
318    /// Consumes `self` and returns the instance key, or `None` if this is a
319    /// full sample.
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
325    /// # let domain = Domain::default();
326    /// # let participant = Participant::new(&domain)?;
327    /// # #[derive(
328    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
329    /// # )]
330    /// # struct Data {
331    /// #     x: i32,
332    /// # }
333    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
334    /// # let reader = Reader::new(&topic)?;
335    /// # let writer = Writer::new(&topic)?;
336    /// # writer.write(&Data::default())?;
337    /// let sample = reader.take()?.into_iter().next().unwrap();
338    /// if let Some(key) = sample.into_key() {
339    ///     println!("key-only notification: {key:?}");
340    /// }
341    /// # Ok::<_, cyclonedds::Error>(())
342    /// ```
343    pub fn into_key(self) -> Option<T::Key> {
344        match self.inner {
345            SampleOrKeyInner::Sample { .. } => None,
346            SampleOrKeyInner::Key { key, .. } => Some(*key),
347        }
348    }
349
350    /// Returns `true` if this is a key-only sample.
351    ///
352    /// # Examples
353    ///
354    /// ```
355    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
356    /// # let domain = Domain::default();
357    /// # let participant = Participant::new(&domain)?;
358    /// # #[derive(
359    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
360    /// # )]
361    /// # struct Data {
362    /// #     x: i32,
363    /// # }
364    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
365    /// # let reader = Reader::new(&topic)?;
366    /// # let writer = Writer::new(&topic)?;
367    /// # writer.write(&Data::default())?;
368    /// let sample = reader.take()?.into_iter().next().unwrap();
369    /// assert!(!sample.is_key());
370    /// # Ok::<_, cyclonedds::Error>(())
371    /// ```
372    pub const fn is_key(&self) -> bool {
373        matches!(self.inner, SampleOrKeyInner::Key { .. })
374    }
375
376    /// Returns `true` if this is a key-only sample and `f` returns `true` for
377    /// its key.
378    ///
379    /// # Examples
380    ///
381    /// ```
382    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
383    /// # let domain = Domain::default();
384    /// # let participant = Participant::new(&domain)?;
385    /// # #[derive(
386    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
387    /// # )]
388    /// # struct Data {
389    /// #     #[dds(key)]
390    /// #     x: i32,
391    /// # }
392    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
393    /// # let reader = Reader::new(&topic)?;
394    /// # let writer = Writer::new(&topic)?;
395    /// # writer.write(&Data::default())?;
396    /// let sample = reader.take()?.into_iter().next().unwrap();
397    /// assert!(!sample.is_key_and(|key| key.x == 1));
398    /// # Ok::<_, cyclonedds::Error>(())
399    /// ```
400    pub fn is_key_and(&self, f: impl FnOnce(&T::Key) -> bool) -> bool {
401        match &self.inner {
402            SampleOrKeyInner::Sample { .. } => false,
403            SampleOrKeyInner::Key { key, .. } => f(key),
404        }
405    }
406
407    /// Returns a borrowed [`View`] of this sample for pattern matching without
408    /// triggering key or sample materialisation.
409    ///
410    /// # Examples
411    ///
412    /// ```
413    /// use cyclonedds::sample::View;
414    /// # use cyclonedds::{Domain, Participant, Topic, Reader, Writer};
415    /// # let domain = Domain::default();
416    /// # let participant = Participant::new(&domain)?;
417    /// # #[derive(
418    /// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
419    /// # )]
420    /// # struct Data {
421    /// #     x: i32,
422    /// # }
423    /// # let topic = Topic::<Data>::new(&participant, "MyTopic")?;
424    /// # let reader = Reader::new(&topic)?;
425    /// # let writer = Writer::new(&topic)?;
426    /// # writer.write(&Data::default())?;
427    ///
428    /// for sample in reader.take()? {
429    ///     match sample.view() {
430    ///         View::Sample(data) => println!("sample: {data:?}"),
431    ///         View::Key(key) => println!("key-only: {key:?}"),
432    ///     }
433    /// }
434    /// # Ok::<_, cyclonedds::Error>(())
435    /// ```
436    pub fn view(&self) -> View<'_, T> {
437        match &self.inner {
438            SampleOrKeyInner::Sample { sample, .. } => View::Sample(sample.as_ref()),
439            SampleOrKeyInner::Key { key, .. } => View::Key(key.as_ref()),
440        }
441    }
442}
443
444impl<T> std::ops::Deref for SampleOrKey<T>
445where
446    T: crate::Topicable,
447{
448    type Target = T;
449
450    fn deref(&self) -> &Self::Target {
451        self.inner.sample()
452    }
453}
454
455/// A borrowed view into a [`SampleOrKey`] for pattern matching.
456///
457/// Obtained via [`SampleOrKey::view`]. Distinguishes between a full sample and
458/// a key-only sample without consuming the [`SampleOrKey`] and without
459/// implicitly materializing the other half.
460///
461/// # Examples
462///
463/// ```
464/// use cyclonedds::sample::View;
465/// # use cyclonedds::{Reader, Topic};
466/// # #[derive(
467/// #     cyclonedds::Topicable, serde::Serialize, serde::Deserialize, Clone, Debug, Default,
468/// # )]
469/// # struct Data {
470/// #     x: i32,
471/// # }
472/// # let domain = cyclonedds::Domain::default();
473/// # let participant = cyclonedds::Participant::new(&domain)?;
474/// # let topic = Topic::<Data>::new(&participant, "MyData")?;
475/// # let reader = Reader::<Data>::new(&topic)?;
476///
477/// for sample in reader.read()? {
478///     match sample.view() {
479///         View::Sample(sample) => println!("got sample: {sample:?}"),
480///         View::Key(key) => println!("got key-only: {key:?}"),
481///     }
482/// }
483/// # Ok::<_, cyclonedds::Error>(())
484/// ```
485pub enum View<'sample, T>
486where
487    T: Topicable,
488{
489    /// A full data sample.
490    Sample(&'sample T),
491    /// A key-only notification, produced when an instance is disposed or
492    /// unregistered.
493    Key(&'sample T::Key),
494}
495
496impl<T> std::fmt::Debug for View<'_, T>
497where
498    T: Topicable,
499    T::Key: std::fmt::Debug,
500{
501    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
502        match self {
503            Self::Sample(sample) => f.debug_tuple("Sample").field(sample).finish(),
504            Self::Key(key) => f.debug_tuple("Key").field(key).finish(),
505        }
506    }
507}
508
509impl<T> std::cmp::PartialEq for View<'_, T>
510where
511    T: Topicable + std::cmp::PartialEq,
512    T::Key: std::cmp::PartialEq,
513{
514    fn eq(&self, other: &Self) -> bool {
515        match (self, other) {
516            (View::Sample(lhs), View::Sample(rhs)) => lhs == rhs,
517            (View::Key(lhs), View::Key(rhs)) => lhs == rhs,
518            _ => false,
519        }
520    }
521}
522
523/// Metadata associated with a received sample.
524///
525/// Attached to every [`SampleOrKey`] and carries the metadata related to the
526/// transmission of the sample.
527///
528/// <div class="warning">
529///
530/// The `valid_data` flag from the DDS specification is not present here as it
531/// is encoded structurally in the type system via [`SampleOrKey`]. A
532/// [`View::Sample`] variant guarantees valid data and a [`View::Key`] variant
533/// guarantees the absence of it.
534///
535/// </div>
536#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
537pub struct Info {
538    /// [`sample`](crate::state::sample), [`view`](crate::state::view), and
539    /// [`instance`](crate::state::instance) state flags at the time of receipt.
540    pub state: crate::State,
541    /// Timestamp at which the sample was written by the publisher.
542    pub source_timestamp: crate::Time,
543    /// Handle identifying the instance this sample belongs to.
544    pub instance_handle: crate::entity::InstanceHandle,
545    /// Handle identifying the writer that published this sample.
546    pub publication_handle: crate::entity::InstanceHandle,
547    /// Number of times the instance was disposed before this sample was
548    /// received.
549    pub disposed_generation_count: u32,
550    /// Number of times the instance transitioned to the no-writers state before
551    /// this sample was received.
552    pub no_writers_generation_count: u32,
553    /// Position of this sample relative to other samples for the same instance
554    /// in the current read or take call.
555    pub sample_rank: u32,
556    /// Difference in generation count between this sample and the most recent
557    /// sample for the same instance in the current read or take call.
558    pub generation_rank: u32,
559    /// Difference in generation count between this sample and the most recent
560    /// sample for the same instance in the reader's cache.
561    pub absolute_generation_rank: u32,
562}
563
564impl From<&cyclonedds_sys::dds_sample_info> for Info {
565    fn from(sample_info: &cyclonedds_sys::dds_sample_info) -> Self {
566        #[allow(clippy::cast_sign_loss, clippy::unnecessary_cast)]
567        let state = crate::State::from_bits_truncate(sample_info.sample_state as u32)
568            | crate::State::from_bits_truncate(sample_info.view_state as u32)
569            | crate::State::from_bits_truncate(sample_info.instance_state as u32);
570        let instance_handle = crate::entity::InstanceHandle {
571            inner: sample_info.instance_handle,
572        };
573        let publication_handle = crate::entity::InstanceHandle {
574            inner: sample_info.publication_handle,
575        };
576        let source_timestamp = crate::Time::from_nanos(sample_info.source_timestamp);
577
578        let disposed_generation_count = sample_info.disposed_generation_count;
579        let no_writers_generation_count = sample_info.no_writers_generation_count;
580        let sample_rank = sample_info.sample_rank;
581        let generation_rank = sample_info.generation_rank;
582        let absolute_generation_rank = sample_info.absolute_generation_rank;
583
584        Self {
585            state,
586            source_timestamp,
587            instance_handle,
588            publication_handle,
589            disposed_generation_count,
590            no_writers_generation_count,
591            sample_rank,
592            generation_rank,
593            absolute_generation_rank,
594        }
595    }
596}
597
598#[cfg(test)]
599mod tests {
600    use super::*;
601
602    fn is_sample_callback(sample: &crate::tests::topic::Data) -> bool {
603        sample.x.is_multiple_of(2)
604    }
605
606    // NOTE: the general interface expects the key to be passed by ref (even if the
607    // key is trivially copyable and small).
608    #[allow(clippy::trivially_copy_pass_by_ref)]
609    fn is_key_callback(key: &(u32, i32)) -> bool {
610        key.0.is_multiple_of(2)
611    }
612
613    #[test]
614    fn test_sample_or_key_sample_ref() {
615        let info = Info {
616            state: crate::State::empty(),
617            source_timestamp: crate::Time::default(),
618            instance_handle: crate::entity::InstanceHandle { inner: 0 },
619            publication_handle: crate::entity::InstanceHandle { inner: 0 },
620            disposed_generation_count: Default::default(),
621            no_writers_generation_count: Default::default(),
622            sample_rank: Default::default(),
623            generation_rank: Default::default(),
624            absolute_generation_rank: Default::default(),
625        };
626        let data = crate::tests::topic::Data {
627            x: 10,
628            y: 11,
629            message: "sample".to_string(),
630        };
631        let sample = SampleOrKey::new_sample(data.clone(), info);
632
633        assert!(sample.is_sample());
634        assert!(sample.is_sample_and(is_sample_callback));
635        assert!(!sample.is_key_and(is_key_callback));
636        assert!(!sample.is_key());
637        assert_eq!(sample.info(), &info);
638        assert_eq!(*sample, data);
639        assert_eq!(sample.sample().unwrap(), &data);
640        assert_eq!(sample.key(), None);
641        assert_eq!(sample.clone().into_sample().unwrap(), data);
642        assert_eq!(sample.clone().into_key(), None);
643    }
644
645    #[test]
646    fn test_sample_or_key_key_ref() {
647        let info = Info {
648            state: crate::State::empty(),
649            source_timestamp: crate::Time::default(),
650            instance_handle: crate::entity::InstanceHandle { inner: 0 },
651            publication_handle: crate::entity::InstanceHandle { inner: 0 },
652            disposed_generation_count: Default::default(),
653            no_writers_generation_count: Default::default(),
654            sample_rank: Default::default(),
655            generation_rank: Default::default(),
656            absolute_generation_rank: Default::default(),
657        };
658        let data = crate::tests::topic::Data {
659            x: 10,
660            y: 11,
661            message: String::new(),
662        };
663        let key = data.as_key();
664        let sample = SampleOrKey::<crate::tests::topic::Data>::new_key(key, info);
665
666        assert!(sample.is_key());
667        assert!(sample.is_key_and(is_key_callback));
668        assert!(!sample.is_sample_and(is_sample_callback));
669        assert!(!sample.is_sample());
670        assert_eq!(sample.info(), &info);
671        assert_eq!(*sample, data);
672        assert_eq!(sample.key().unwrap(), &key);
673        assert_eq!(sample.sample(), None);
674        assert_eq!(sample.clone().into_key().unwrap(), key);
675        assert_eq!(sample.clone().into_sample(), None);
676    }
677
678    #[test]
679    fn test_sample_or_key_view() {
680        let info = Info {
681            state: crate::State::empty(),
682            source_timestamp: crate::Time::default(),
683            instance_handle: crate::entity::InstanceHandle { inner: 0 },
684            publication_handle: crate::entity::InstanceHandle { inner: 0 },
685            disposed_generation_count: Default::default(),
686            no_writers_generation_count: Default::default(),
687            sample_rank: Default::default(),
688            generation_rank: Default::default(),
689            absolute_generation_rank: Default::default(),
690        };
691        let sample_data = crate::tests::topic::Data {
692            x: 10,
693            y: 11,
694            message: "sample".to_string(),
695        };
696        let sample_key = sample_data.as_key();
697
698        let sample =
699            SampleOrKey::<crate::tests::topic::Data>::new_sample(sample_data.clone(), info);
700        let key = SampleOrKey::<crate::tests::topic::Data>::new_key(sample_key, info);
701
702        let sample_display = format!("{sample:?}");
703        let key_display = format!("{key:?}");
704        assert!(sample_display.contains(&format!("{sample_data:?}")));
705        assert!(key_display.contains(&format!("{sample_key:?}")));
706        assert!(sample_display.contains(&format!("{sample_data:?}")));
707        assert!(sample_display.contains(&format!("{info:?}")));
708        assert!(key_display.contains(&format!("{sample_key:?}")));
709        assert!(key_display.contains(&format!("{info:?}")));
710
711        let view_sample_display = format!("{:?}", sample.view());
712        let view_key_display = format!("{:?}", key.view());
713        assert!(view_sample_display.contains(&format!("{sample_data:?}")));
714        assert!(view_key_display.contains(&format!("{sample_key:?}")));
715        assert!(view_sample_display.contains(&format!("{sample_data:?}")));
716
717        assert!(sample.view() != key.view());
718        assert_eq!(sample.view(), View::Sample(&sample_data));
719        assert_eq!(key.view(), View::Key(&sample_key));
720    }
721}