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}