Skip to main content

zerodds_qos/policies/
presentation.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! PresentationQosPolicy (DDS 1.4 §2.2.3.6).
4//!
5//! Wire-Format: u32 access_scope + bool coherent + bool ordered =
6//! 4 + 1 + 1 + padding = 8 byte (4-byte aligned).
7
8use zerodds_cdr::{BufferReader, BufferWriter, DecodeError, EncodeError};
9
10/// Presentation-Access-Scope.
11///
12/// Compatibility per §2.2.3.6.6: `offered.access_scope >= requested.access_scope`
13/// (INSTANCE < TOPIC < GROUP).
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
15#[repr(u32)]
16pub enum PresentationAccessScope {
17    /// Instance-Scope (default).
18    #[default]
19    Instance = 0,
20    /// Topic-Scope.
21    Topic = 1,
22    /// Group-Scope (Publisher/Subscriber-weit).
23    Group = 2,
24}
25
26impl PresentationAccessScope {
27    /// Strikter Mapper.
28    #[must_use]
29    pub const fn try_from_u32(v: u32) -> Option<Self> {
30        match v {
31            0 => Some(Self::Instance),
32            1 => Some(Self::Topic),
33            2 => Some(Self::Group),
34            _ => None,
35        }
36    }
37
38    /// Forward-kompatibler Mapper.
39    #[must_use]
40    pub const fn from_u32(v: u32) -> Self {
41        match v {
42            1 => Self::Topic,
43            2 => Self::Group,
44            _ => Self::Instance,
45        }
46    }
47}
48
49/// PresentationQosPolicy.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
51pub struct PresentationQosPolicy {
52    /// Access-Scope.
53    pub access_scope: PresentationAccessScope,
54    /// Coherent-Access.
55    pub coherent_access: bool,
56    /// Ordered-Access.
57    pub ordered_access: bool,
58}
59
60impl PresentationQosPolicy {
61    /// Wire-Encoding.
62    ///
63    /// # Errors
64    /// Buffer-Overflow.
65    pub fn encode_into(self, w: &mut BufferWriter) -> Result<(), EncodeError> {
66        w.write_u32(self.access_scope as u32)?;
67        w.write_u8(u8::from(self.coherent_access))?;
68        w.write_u8(u8::from(self.ordered_access))?;
69        // 2 byte padding auf 4-byte-Alignment.
70        w.write_u8(0)?;
71        w.write_u8(0)
72    }
73
74    /// Wire-Decoding (strict).
75    ///
76    /// # Errors
77    /// Buffer-Underflow oder unbekannter AccessScope-Wert.
78    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
79        let v = r.read_u32()?;
80        let access_scope =
81            PresentationAccessScope::try_from_u32(v).ok_or(DecodeError::InvalidEnum {
82                kind: "PresentationAccessScope",
83                value: v,
84            })?;
85        let coherent_access = r.read_u8()? != 0;
86        let ordered_access = r.read_u8()? != 0;
87        let _pad1 = r.read_u8()?;
88        let _pad2 = r.read_u8()?;
89        Ok(Self {
90            access_scope,
91            coherent_access,
92            ordered_access,
93        })
94    }
95}
96
97#[cfg(test)]
98#[allow(clippy::unwrap_used)]
99mod tests {
100    use super::*;
101    use zerodds_cdr::Endianness;
102
103    #[test]
104    fn default_instance_no_flags() {
105        let d = PresentationQosPolicy::default();
106        assert_eq!(d.access_scope, PresentationAccessScope::Instance);
107        assert!(!d.coherent_access);
108        assert!(!d.ordered_access);
109    }
110
111    #[test]
112    fn scope_ordering() {
113        use PresentationAccessScope::*;
114        assert!(Instance < Topic);
115        assert!(Topic < Group);
116    }
117
118    #[test]
119    fn try_from_u32_strict() {
120        assert_eq!(
121            PresentationAccessScope::try_from_u32(0),
122            Some(PresentationAccessScope::Instance)
123        );
124        assert_eq!(
125            PresentationAccessScope::try_from_u32(2),
126            Some(PresentationAccessScope::Group)
127        );
128        assert_eq!(PresentationAccessScope::try_from_u32(5), None);
129    }
130
131    #[test]
132    fn from_u32_forward_compat() {
133        assert_eq!(
134            PresentationAccessScope::from_u32(99),
135            PresentationAccessScope::Instance
136        );
137        assert_eq!(
138            PresentationAccessScope::from_u32(1),
139            PresentationAccessScope::Topic
140        );
141    }
142
143    #[test]
144    fn roundtrip_instance_no_flags() {
145        let p = PresentationQosPolicy::default();
146        let mut w = BufferWriter::new(Endianness::Little);
147        p.encode_into(&mut w).unwrap();
148        let bytes = w.into_bytes();
149        let mut r = BufferReader::new(&bytes, Endianness::Little);
150        assert_eq!(PresentationQosPolicy::decode_from(&mut r).unwrap(), p);
151    }
152
153    #[test]
154    fn roundtrip_all_flags() {
155        let p = PresentationQosPolicy {
156            access_scope: PresentationAccessScope::Group,
157            coherent_access: true,
158            ordered_access: true,
159        };
160        let mut w = BufferWriter::new(Endianness::Little);
161        p.encode_into(&mut w).unwrap();
162        let bytes = w.into_bytes();
163        assert_eq!(bytes.len(), 8);
164        let mut r = BufferReader::new(&bytes, Endianness::Little);
165        assert_eq!(PresentationQosPolicy::decode_from(&mut r).unwrap(), p);
166    }
167}