Skip to main content

zerodds_ccm/
model.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! `Components::*` Core-Types Model — Spec §6.4-§6.7 + §6.10.
5//!
6//! Spec §6.4.3.3 (S. 15) + §6.5.2.4 (S. 21) + §6.5.3 (S. 22) + §6.6.1.2
7//! (S. 25) + §6.6.8 (S. 29) + §6.7.6 (S. 40) definieren das `Components`-
8//! Modul. Wir modellieren die Daten-Wertobjekte als plain Rust-Structs,
9//! sodass jede Codegen-Pipeline diese als ScopedNames referenzieren kann
10//! ohne ein "Components.idl"-File explizit einlesen zu muessen.
11
12use alloc::string::String;
13use alloc::vec::Vec;
14
15/// `Components::FeatureName` — Spec §6.4.3.3 (S. 15) — `typedef string
16/// FeatureName;`.
17pub type FeatureName = String;
18
19/// `CORBA::RepositoryId` — Spec §6.4.3.3 (S. 15) referenziert.
20pub type RepositoryId = String;
21
22/// `Components::FailureReason` — Spec §6.7.6 (S. 40) — `typedef
23/// unsigned long FailureReason;`.
24pub type FailureReason = u32;
25
26/// `Components::Cookie`-valuetype — Spec §6.5.2.4 (S. 21).
27///
28/// Spec-IDL:
29/// ```idl
30/// module Components {
31///     valuetype Cookie {
32///         private CORBA::OctetSeq cookieValue;
33///     };
34/// };
35/// ```
36///
37/// Cookies werden von Multiplex-Receptacles erzeugt und identifizieren
38/// eine konkrete Connection auf dem Receptacle (Spec §6.5.2.4).
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct Cookie {
41    /// `private CORBA::OctetSeq cookieValue;` — opaque Bytes,
42    /// Receptacle-implementation-defined.
43    pub cookie_value: Vec<u8>,
44}
45
46impl Cookie {
47    /// Konstruktor — Spec §6.5.2.4 (S. 21).
48    #[must_use]
49    pub fn new(cookie_value: Vec<u8>) -> Self {
50        Self { cookie_value }
51    }
52
53    /// Spec §6.5.2.4 (S. 22): "any derived cookie types shall be
54    /// truncatable to Cookie, and the information preserved in the
55    /// cookieValue octet sequence shall be sufficient for the receptacle
56    /// implementation to identify the cookie and its associated
57    /// connected reference."
58    #[must_use]
59    pub fn truncate_to_base(&self) -> Self {
60        // Bei der Truncation bleibt der Octet-Sequence erhalten — das ist
61        // die normative Garantie. Derived-Subtype-State geht verloren.
62        Self {
63            cookie_value: self.cookie_value.clone(),
64        }
65    }
66}
67
68/// `Components::PortDescription`-valuetype — Spec §6.4.3.3 (S. 15).
69///
70/// Base-valuetype fuer FacetDescription, ReceptacleDescription,
71/// ConsumerDescription, EmitterDescription, PublisherDescription
72/// (jeweils im jeweiligen Spec-Abschnitt §6.4.3.3 / §6.5.3 / §6.6.8).
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct PortDescription {
75    /// Port-Name (Spec FeatureName).
76    pub name: FeatureName,
77    /// CORBA-Repository-ID des Port-Interface-Typs.
78    pub type_id: RepositoryId,
79}
80
81/// `Components::FacetDescription : PortDescription` — Spec §6.4.3.3
82/// (S. 15).
83///
84/// `valuetype FacetDescription : PortDescription { public Object
85/// facet_ref; };` — `facet_ref` ist in unserem ORB-freien Kontext eine
86/// abstrakte Object-Reference (modelliert als Repository-ID-String).
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct FacetDescription {
89    /// Inherited port description.
90    pub base: PortDescription,
91    /// `Object facet_ref;` — opaque Object-Reference-Identifier.
92    pub facet_ref: RepositoryId,
93}
94
95/// `Components::ConnectionDescription`-valuetype — Spec §6.5.3 (S. 22).
96///
97/// `valuetype ConnectionDescription { public Cookie ck; public Object
98/// objref; };`.
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct ConnectionDescription {
101    /// Cookie der Connection (bei Multiplex; bei Simplex `default`).
102    pub cookie: Cookie,
103    /// Verbundener Object-Reference (Repository-ID-Identifier).
104    pub objref: RepositoryId,
105}
106
107/// `Components::ReceptacleDescription : PortDescription` — Spec §6.5.3
108/// (S. 22).
109#[derive(Debug, Clone, PartialEq, Eq)]
110pub struct ReceptacleDescription {
111    /// Inherited port description.
112    pub base: PortDescription,
113    /// `boolean is_multiple;`.
114    pub is_multiple: bool,
115    /// `ConnectionDescriptions connections;`.
116    pub connections: Vec<ConnectionDescription>,
117}
118
119/// `Components::ConsumerDescription : PortDescription` — Spec §6.6.8
120/// (S. 30).
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct ConsumerDescription {
123    /// Inherited port description.
124    pub base: PortDescription,
125    /// `EventConsumerBase consumer;` — opaque Object-Reference.
126    pub consumer: RepositoryId,
127}
128
129/// `Components::EmitterDescription : PortDescription` — Spec §6.6.8
130/// (S. 30).
131#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct EmitterDescription {
133    /// Inherited port description.
134    pub base: PortDescription,
135    /// `EventConsumerBase consumer;` — gegenstueck-Endpunkt.
136    pub consumer: RepositoryId,
137}
138
139/// `Components::SubscriberDescription`-valuetype — Spec §6.6.8 (S. 30).
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct SubscriberDescription {
142    /// Cookie unter dem die Subscription registriert ist.
143    pub cookie: Cookie,
144    /// `EventConsumerBase consumer;`.
145    pub consumer: RepositoryId,
146}
147
148/// `Components::PublisherDescription : PortDescription` — Spec §6.6.8
149/// (S. 30).
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct PublisherDescription {
152    /// Inherited port description.
153    pub base: PortDescription,
154    /// `SubscriberDescriptions consumers;`.
155    pub consumers: Vec<SubscriberDescription>,
156}
157
158/// `Components::ConfigValue`-valuetype — Spec §6.10.1.2 (S. 45).
159///
160/// `valuetype ConfigValue { public FeatureName name; public any value;
161/// };`. Wir modellieren `any` als opaque Bytes (CDR-marshaled).
162#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct ConfigValue {
164    /// Attribut-Name.
165    pub name: FeatureName,
166    /// CDR-marshaled `any`-Wert.
167    pub value: Vec<u8>,
168}
169
170#[cfg(test)]
171#[allow(clippy::expect_used)]
172mod tests {
173    use super::*;
174
175    #[test]
176    fn cookie_new_stores_octet_seq() {
177        let c = Cookie::new(alloc::vec![1, 2, 3]);
178        assert_eq!(c.cookie_value, alloc::vec![1, 2, 3]);
179    }
180
181    #[test]
182    fn cookie_truncate_preserves_octet_seq() {
183        // Spec §6.5.2.4 (S. 22): "information preserved in the
184        // cookieValue octet sequence shall be sufficient".
185        let c = Cookie::new(alloc::vec![0xDE, 0xAD]);
186        let t = c.truncate_to_base();
187        assert_eq!(t.cookie_value, c.cookie_value);
188    }
189
190    #[test]
191    fn port_description_carries_name_and_type_id() {
192        let p = PortDescription {
193            name: String::from("foo"),
194            type_id: String::from("IDL:M/I:1.0"),
195        };
196        assert_eq!(p.name, "foo");
197        assert_eq!(p.type_id, "IDL:M/I:1.0");
198    }
199
200    #[test]
201    fn receptacle_description_supports_simplex_and_multiplex() {
202        // Spec §6.5.3 (S. 22) — `is_multiple` differenziert.
203        let simplex = ReceptacleDescription {
204            base: PortDescription {
205                name: String::from("manager"),
206                type_id: String::from("IDL:Stock/StockManager:1.0"),
207            },
208            is_multiple: false,
209            connections: alloc::vec![],
210        };
211        assert!(!simplex.is_multiple);
212        assert!(simplex.connections.is_empty());
213
214        let multiplex = ReceptacleDescription {
215            base: PortDescription {
216                name: String::from("managers"),
217                type_id: String::from("IDL:Stock/StockManager:1.0"),
218            },
219            is_multiple: true,
220            connections: alloc::vec![
221                ConnectionDescription {
222                    cookie: Cookie::new(alloc::vec![1]),
223                    objref: String::from("ref-A"),
224                },
225                ConnectionDescription {
226                    cookie: Cookie::new(alloc::vec![2]),
227                    objref: String::from("ref-B"),
228                }
229            ],
230        };
231        assert!(multiplex.is_multiple);
232        assert_eq!(multiplex.connections.len(), 2);
233    }
234
235    #[test]
236    fn publisher_description_can_have_multiple_subscribers() {
237        // Spec §6.6.5 (S. 27) "multiple subscribers".
238        let p = PublisherDescription {
239            base: PortDescription {
240                name: String::from("ticker"),
241                type_id: String::from("IDL:Stock/Tick:1.0"),
242            },
243            consumers: alloc::vec![
244                SubscriberDescription {
245                    cookie: Cookie::new(alloc::vec![10]),
246                    consumer: String::from("sub-A"),
247                },
248                SubscriberDescription {
249                    cookie: Cookie::new(alloc::vec![11]),
250                    consumer: String::from("sub-B"),
251                }
252            ],
253        };
254        assert_eq!(p.consumers.len(), 2);
255    }
256
257    #[test]
258    fn config_value_carries_name_and_marshaled_value() {
259        // Spec §6.10.1.2 (S. 45).
260        let cv = ConfigValue {
261            name: String::from("rate_hz"),
262            value: alloc::vec![0, 0, 0, 100],
263        };
264        assert_eq!(cv.name, "rate_hz");
265        assert_eq!(cv.value.len(), 4);
266    }
267}