Skip to main content

zerodds_dcps/
sample.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! `Sample<T>` — data + [`SampleInfo`] together.
4//!
5//! Spec reference: OMG DDS-DCPS 1.4 §2.2.2.5.3 `read`/`take`. The spec
6//! works with two parallel sequences `data_values` and `sample_infos`;
7//! in our Rust API we bundle them into a single `Sample<T> { data, info }`.
8//!
9//! ## valid_data
10//!
11//! When `info.valid_data == false`, the sample carries pure lifecycle
12//! information (dispose/unregister marker). In that case `data` holds a
13//! default-constructed `T` with only the key fields filled in
14//! (spec §2.2.2.5.1.13).
15
16extern crate alloc;
17
18use crate::sample_info::SampleInfo;
19
20/// A single sample record from `read`/`take`.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct Sample<T> {
23    /// Application data. When `info.valid_data == false`, only the key
24    /// portion is meaningful.
25    pub data: T,
26    /// Metadata (spec §2.2.2.5.1).
27    pub info: SampleInfo,
28}
29
30impl<T> Sample<T> {
31    /// Constructor.
32    #[must_use]
33    pub fn new(data: T, info: SampleInfo) -> Self {
34        Self { data, info }
35    }
36
37    /// `true` if the sample carries payload (not just a lifecycle
38    /// marker).
39    #[must_use]
40    pub fn is_valid(&self) -> bool {
41        self.info.valid_data
42    }
43
44    /// Convenience accessor: lifecycle view without data.
45    #[must_use]
46    pub fn info(&self) -> &SampleInfo {
47        &self.info
48    }
49}
50
51#[cfg(test)]
52#[allow(clippy::expect_used, clippy::unwrap_used)]
53mod tests {
54    use super::*;
55    use crate::instance_handle::InstanceHandle;
56    use crate::sample_info::{InstanceStateKind, SampleStateKind, ViewStateKind};
57    use crate::time::Time;
58
59    #[test]
60    fn sample_new_holds_data_and_info() {
61        let info = SampleInfo::new_alive(
62            InstanceHandle::from_raw(1),
63            InstanceHandle::from_raw(2),
64            Time::new(10, 0),
65        );
66        let s = Sample::new(42u32, info);
67        assert_eq!(s.data, 42);
68        assert_eq!(s.info().instance_handle, InstanceHandle::from_raw(1));
69        assert!(s.is_valid());
70    }
71
72    #[test]
73    fn sample_with_invalid_data_marker() {
74        let info = SampleInfo {
75            valid_data: false,
76            instance_state: InstanceStateKind::NotAliveDisposed,
77            ..SampleInfo::default()
78        };
79        let s = Sample::new(0u32, info);
80        assert!(!s.is_valid());
81        assert_eq!(s.info.instance_state, InstanceStateKind::NotAliveDisposed);
82    }
83
84    #[test]
85    fn sample_states_in_info_default() {
86        let s = Sample::new((), SampleInfo::default());
87        assert_eq!(s.info.sample_state, SampleStateKind::NotRead);
88        assert_eq!(s.info.view_state, ViewStateKind::New);
89    }
90}