Skip to main content

zerodds_dcps/
interop.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Interop-Test-Typen fuer Cross-Vendor-Nachweis.
4//!
5//! Enthaelt Application-Typen, die in der DDS-Welt als de-facto-
6//! Interop-Benchmark gelten — allen voran `ShapeType` aus dem
7//! RTI/Cyclone/Fast-DDS-ShapesDemo. Diese Typen sind nicht fuer
8//! Produktions-Nutzung gedacht, sondern dafuer, gegenueber anderen
9//! DDS-Stacks zu beweisen, dass unsere Wire- und Typen-Semantik
10//! byte-kompatibel ist.
11//!
12//! # `ShapeType`
13//!
14//! Spec-Grundlage (IDL, wie von RTI/Cyclone/Fast-DDS verwendet):
15//!
16//! ```idl
17//! struct ShapeType {
18//!     @key string<128> color;
19//!     int32 x;
20//!     int32 y;
21//!     int32 shapesize;
22//! };
23//! ```
24//!
25//! Encoding: XCDR2 Little-Endian (das ist die Standard-Einstellung
26//! saemtlicher ShapesDemo-Implementierungen, und entspricht unserem
27//! User-Payload-Encapsulation-Header `0x00 0x07 0x00 0x00`).
28//!
29//! CDR-Layout:
30//! ```text
31//! offset 0  : uint32   color.length (inkl. null-terminator)
32//! offset 4  : bytes    color.utf8_bytes
33//! offset 4+n: uint8    0x00          (null-terminator)
34//! padding            (auf naechste 4-Byte-Grenze)
35//! offset *  : int32    x
36//! offset *+4: int32    y
37//! offset *+8: int32    shapesize
38//! ```
39//!
40//! Der `@key` auf `color` wird auf Wire-Level nicht gesondert behandelt
41//! — er steuert Instance-Keying, nicht die Serialisierung. Fuer unser
42//! Sample-Matching in v1.2 (noch keine Instance-Map im Reader) ist
43//! jede (color, x, y, shapesize)-Kombination effektiv ein eigenes
44//! Sample.
45
46extern crate alloc;
47
48use alloc::string::String;
49use alloc::vec::Vec;
50
51use zerodds_cdr::buffer::{BufferReader, BufferWriter};
52use zerodds_cdr::endianness::Endianness;
53
54use crate::dds_type::{DdsType, DecodeError, EncodeError};
55
56/// RTI / Cyclone / Fast-DDS ShapesDemo-kompatibler Application-Type.
57///
58/// Siehe Modul-Doku fuer Spec und Layout. Farbe ist Instance-Key,
59/// x/y/shapesize sind die typischen Formen-Koordinaten.
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct ShapeType {
62    /// Farbe / Instance-Key. In ShapesDemo-Implementierungen typisch
63    /// `"BLUE"`, `"RED"`, `"GREEN"`, `"YELLOW"`, `"MAGENTA"`, `"CYAN"`,
64    /// `"ORANGE"`, `"PURPLE"`. Keine inhaltliche Validierung hier —
65    /// beliebige UTF-8-Strings sind zulaessig.
66    pub color: String,
67    /// X-Koordinate in Pixeln (ShapesDemo-Canvas ist ~240×270).
68    pub x: i32,
69    /// Y-Koordinate.
70    pub y: i32,
71    /// Formen-Groesse in Pixeln. Typisch 30.
72    pub shapesize: i32,
73}
74
75impl ShapeType {
76    /// Konstruktor.
77    #[must_use]
78    pub fn new(color: impl Into<String>, x: i32, y: i32, shapesize: i32) -> Self {
79        Self {
80            color: color.into(),
81            x,
82            y,
83            shapesize,
84        }
85    }
86}
87
88impl DdsType for ShapeType {
89    /// Type-Name **exakt** wie RTI/Cyclone/Fast-DDS ShapesDemo. Aendern
90    /// wuerde Matching mit jedem anderen ShapesDemo-Client brechen.
91    const TYPE_NAME: &'static str = "ShapeType";
92    /// ShapesDemo-IDL: `@key string color`. ShapeType ist keyed —
93    /// Per-Instance-QoS (TimeBasedFilter, Ownership, Lifecycle)
94    /// haengt davon ab.
95    const HAS_KEY: bool = true;
96
97    fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
98        holder.write_string(&self.color);
99    }
100
101    fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
102        let mut w = BufferWriter::new(Endianness::Little);
103        w.write_string(&self.color)
104            .map_err(|_| EncodeError::Invalid {
105                what: "ShapeType.color encoding",
106            })?;
107        w.write_u32(self.x as u32)
108            .map_err(|_| EncodeError::Invalid {
109                what: "ShapeType.x encoding",
110            })?;
111        w.write_u32(self.y as u32)
112            .map_err(|_| EncodeError::Invalid {
113                what: "ShapeType.y encoding",
114            })?;
115        w.write_u32(self.shapesize as u32)
116            .map_err(|_| EncodeError::Invalid {
117                what: "ShapeType.shapesize encoding",
118            })?;
119        out.extend_from_slice(w.as_bytes());
120        Ok(())
121    }
122
123    fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
124        let mut r = BufferReader::new(bytes, Endianness::Little);
125        let color = r.read_string().map_err(|_| DecodeError::Invalid {
126            what: "ShapeType.color decoding",
127        })?;
128        let x = r.read_u32().map_err(|_| DecodeError::Invalid {
129            what: "ShapeType.x decoding",
130        })? as i32;
131        let y = r.read_u32().map_err(|_| DecodeError::Invalid {
132            what: "ShapeType.y decoding",
133        })? as i32;
134        let shapesize = r.read_u32().map_err(|_| DecodeError::Invalid {
135            what: "ShapeType.shapesize decoding",
136        })? as i32;
137        Ok(Self {
138            color,
139            x,
140            y,
141            shapesize,
142        })
143    }
144}