zerodds_corba_rust/runtime.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Runtime-Types die der generierte Code referenziert.
4//!
5//! Phase-1-Skelett. Volle Wire-Implementation (GIOP-Marshalling +
6//! IIOP-Connection) folgt in Phase-2 ueber `corba-iiop` und
7//! `corba-giop`. Hier liegen nur die Public-API-Strukturen die der
8//! emittierte Stub/Skeleton/Valuetype referenziert.
9
10extern crate alloc;
11use alloc::string::String;
12use alloc::vec::Vec;
13
14/// CORBA-Object-Reference (IOR-encoded). Generierte Stubs halten eine
15/// Instanz dieser Struct als Connection-Handle.
16#[derive(Debug, Clone, PartialEq, Eq, Default)]
17pub struct ObjectReference {
18 /// Type-Identifier (Repository-ID, z.B.
19 /// `IDL:omg.org/MyInterface:1.0`).
20 pub type_id: String,
21 /// IIOP-Profile-Bytes (CDR-encoded). Phase-1 ist das opaque;
22 /// Phase-2 nutzt corba-iiop fuer die echte Connection.
23 pub iiop_profile: Vec<u8>,
24}
25
26/// CORBA-System-/User-Exception. Generierte Stubs/Skeletons mappen
27/// alle Fehlerpfade auf diese Variant.
28#[derive(Debug, Clone, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum CorbaException {
31 /// CORBA-System-Exception (Spec §3.17.1). Minor-Code laut OMG.
32 SystemException {
33 /// Minor-Code aus dem Spec-Standard (z.B. `0x4F4D000B` =
34 /// `BAD_OPERATION`).
35 minor: u32,
36 /// Statische Fehler-Beschreibung.
37 message: &'static str,
38 },
39 /// User-Exception aus IDL `exception E { ... };`. Wir tragen den
40 /// Repository-ID + Payload-Bytes (Phase-1-MVP).
41 UserException {
42 /// Repository-ID des Exception-Types (z.B.
43 /// `IDL:omg.org/MyError:1.0`).
44 repository_id: String,
45 /// Payload-Bytes (XCDR-encoded).
46 payload: Vec<u8>,
47 },
48}
49
50impl ::core::fmt::Display for CorbaException {
51 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
52 match self {
53 Self::SystemException { minor, message } => {
54 write!(f, "CORBA::SystemException(minor={minor}): {message}")
55 }
56 Self::UserException { repository_id, .. } => {
57 write!(f, "CORBA::UserException({repository_id})")
58 }
59 }
60 }
61}
62
63impl ::std::error::Error for CorbaException {}
64
65/// ValueBase-Trait. Alle IDL-Valuetypes erweitern diesen.
66pub trait ValueBase {
67 /// Repository-ID des Valuetypes (z.B. `IDL:omg.org/MyValue:1.0`).
68 fn repository_id(&self) -> &str;
69}
70
71/// Result eines Skeleton-Dispatches.
72#[derive(Debug, Clone, PartialEq, Eq)]
73#[non_exhaustive]
74pub enum SkeletonResult {
75 /// Operation erfolgreich, Reply-Bytes liegen vor.
76 Reply(Vec<u8>),
77 /// Operation hat eine User- oder System-Exception geworfen.
78 Exception(CorbaException),
79 /// Unbekannter Operation-Name fuer das Interface — der POA
80 /// muss BAD_OPERATION zurueckgeben.
81 BadOperation,
82 /// Operation ist deklariert aber Wire-Marshalling ist Phase-2 —
83 /// vorerst Platzhalter.
84 NotYetWired,
85}
86
87/// POA-Servant-Marker. Concrete Server-Implementierungen impl-en das
88/// plus den jeweiligen IDL-Interface-Trait.
89pub trait Servant {
90 /// Repository-ID des Interface, das der Servant implementiert.
91 fn target_repository_id(&self) -> &str;
92}
93
94// ============================================================================
95// §2.4 TypeCode (CORBA 3.3 §10.7) — minimal-funktionaler Wrapper.
96// ============================================================================
97
98/// CORBA `TypeCode` — Type-Reflection-Wrapper.
99///
100/// Vollstaendige TypeCode-Operationen (kind, member_count, member_name,
101/// content_type, ...) werden ueber `corba-iiop`-IIOP-Connection
102/// ausgewertet — Phase-1 ist hier ein Wrapper um den Repository-ID,
103/// Phase-2 erweitert mit Lookup-API.
104#[derive(Debug, Clone, PartialEq, Eq, Default)]
105pub struct TypeCode {
106 /// Repository-ID des referenzierten Types.
107 pub repository_id: String,
108}
109
110impl TypeCode {
111 /// Konstruiert einen TypeCode aus einem Repository-ID.
112 #[must_use]
113 pub fn new(repository_id: impl Into<String>) -> Self {
114 Self {
115 repository_id: repository_id.into(),
116 }
117 }
118}
119
120// ============================================================================
121// §7.2 POA-Configuration-Builder (Spec §11.3 — 7 Policies)
122// ============================================================================
123
124/// CORBA-POA-Builder fuer Server-Side-Object-Aktivierung.
125///
126/// Spec §11.3 definiert 7 Policies; alle haben sinnvolle Defaults
127/// fuer typische Embedded-/Server-Use-Cases. End-User
128/// ueberschreibt selektiv via `with_*`-Methoden und ruft `build()`
129/// fuer den finalen `PoaConfiguration`.
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct PoaBuilder {
132 /// Threading-Policy (Spec §11.3.4).
133 pub thread_policy: ThreadPolicy,
134 /// Lifespan-Policy (§11.3.5).
135 pub lifespan_policy: LifespanPolicy,
136 /// IdAssignment-Policy (§11.3.6).
137 pub id_assignment_policy: IdAssignmentPolicy,
138 /// IdUniqueness-Policy (§11.3.7).
139 pub id_uniqueness_policy: IdUniquenessPolicy,
140 /// ImplicitActivation-Policy (§11.3.8).
141 pub implicit_activation_policy: ImplicitActivationPolicy,
142 /// ServantRetention-Policy (§11.3.9).
143 pub servant_retention_policy: ServantRetentionPolicy,
144 /// RequestProcessing-Policy (§11.3.10).
145 pub request_processing_policy: RequestProcessingPolicy,
146}
147
148impl Default for PoaBuilder {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154impl PoaBuilder {
155 /// Erzeugt einen neuen POA-Builder mit Spec-Default-Policies.
156 #[must_use]
157 pub fn new() -> Self {
158 Self {
159 thread_policy: ThreadPolicy::OrbCtrlModel,
160 lifespan_policy: LifespanPolicy::Transient,
161 id_assignment_policy: IdAssignmentPolicy::SystemId,
162 id_uniqueness_policy: IdUniquenessPolicy::Unique,
163 implicit_activation_policy: ImplicitActivationPolicy::NoImplicitActivation,
164 servant_retention_policy: ServantRetentionPolicy::Retain,
165 request_processing_policy: RequestProcessingPolicy::UseActiveObjectMapOnly,
166 }
167 }
168}
169
170/// POA-Threading-Policy (Spec §11.3.4).
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum ThreadPolicy {
173 /// Default: ORB waehlt Threading.
174 OrbCtrlModel,
175 /// Single-Threaded.
176 SingleThreadModel,
177 /// Main-Thread-Only.
178 MainThreadModel,
179}
180
181/// POA-Lifespan-Policy (Spec §11.3.5).
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum LifespanPolicy {
184 /// Default: Object-Refs gelten nur fuer ORB-Lifetime.
185 Transient,
186 /// Object-Refs ueberleben ORB-Restart.
187 Persistent,
188}
189
190/// POA-IdAssignment-Policy (§11.3.6).
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum IdAssignmentPolicy {
193 /// Default: ORB vergibt Object-IDs.
194 SystemId,
195 /// Anwendung vergibt Object-IDs.
196 UserId,
197}
198
199/// POA-IdUniqueness-Policy (§11.3.7).
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub enum IdUniquenessPolicy {
202 /// Default: jeder Servant hat genau eine Object-ID.
203 Unique,
204 /// Servant kann mehrere Object-IDs haben.
205 Multiple,
206}
207
208/// POA-ImplicitActivation-Policy (§11.3.8).
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210pub enum ImplicitActivationPolicy {
211 /// Default: explicit `activate_object` Pflicht.
212 NoImplicitActivation,
213 /// Auto-activation bei ersten Method-Call.
214 ImplicitActivation,
215}
216
217/// POA-ServantRetention-Policy (§11.3.9).
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum ServantRetentionPolicy {
220 /// Default: ActiveObjectMap haelt Servants.
221 Retain,
222 /// Stateless — pro Request frischer Servant.
223 NonRetain,
224}
225
226/// POA-RequestProcessing-Policy (§11.3.10).
227#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228pub enum RequestProcessingPolicy {
229 /// Default: ActiveObjectMap-Lookup, sonst BAD_OPERATION.
230 UseActiveObjectMapOnly,
231 /// Lookup, dann ServantManager-Fallback.
232 UseDefaultServant,
233 /// Lookup, dann ServantActivator/-Locator.
234 UseServantManager,
235}
236
237// ============================================================================
238// §4.4 Valuetype Wire-Marshalling — minimaler Stream-API
239// ============================================================================
240
241/// Stream-API fuer ValueBase-Wire-Marshalling (CDR §15.3.4).
242///
243/// Phase-1: API-Skelett, das `zerodds_cdr::BufferReader/Writer` wraped und
244/// die value-tag-Logic anwendet (chunked encoding, repository-id-list).
245/// Phase-2 wired die volle Spec-Konformitaet (truncatable, custom
246/// marshaling, codeset).
247pub struct ValueStreamWriter<'a> {
248 /// Inner-Writer.
249 pub inner: &'a mut zerodds_cdr::BufferWriter,
250}
251
252impl<'a> ValueStreamWriter<'a> {
253 /// Konstruktor.
254 pub fn new(inner: &'a mut zerodds_cdr::BufferWriter) -> Self {
255 Self { inner }
256 }
257
258 /// Schreibt einen value-tag (CDR §15.3.4.2). Format: 0x7FFFFF02
259 /// fuer single-repository-id, 0x7FFFFF06 fuer list-of-repository-
260 /// ids, 0x00000000 fuer null-value.
261 ///
262 /// # Errors
263 /// Encode-Fehler.
264 pub fn write_value_tag(
265 &mut self,
266 repository_id: &str,
267 ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
268 // Single-Repository-ID-Tag (chunked = false, codeset = false).
269 self.inner.write_u32(0x7FFF_FF02)?;
270 self.inner.write_string(repository_id)?;
271 Ok(())
272 }
273
274 /// Schreibt einen value-tag mit multi-repository-id-Liste (CDR
275 /// §15.3.4.2). Wire-Tag = 0x7FFF_FF06 (`list-of-repository-ids` +
276 /// kein Chunked-Bit). Layout:
277 ///
278 /// ```text
279 /// u32 tag = 0x7FFFFF06
280 /// i32 count // > 0
281 /// string id[0] .. id[N-1] // CDR-strings (length + bytes + NUL)
282 /// ```
283 ///
284 /// # Errors
285 /// Encode-Fehler oder leere `repository_ids` (Spec verlangt N >= 1).
286 pub fn write_value_tag_multi(
287 &mut self,
288 repository_ids: &[&str],
289 ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
290 debug_assert!(
291 !repository_ids.is_empty(),
292 "Spec §15.3.4.2: list-of-repository-ids must contain >=1 entry"
293 );
294 self.inner.write_u32(0x7FFF_FF06)?;
295 // count als signed long.
296 let count: i32 = repository_ids.len() as i32;
297 self.inner.write_u32(count as u32)?;
298 for id in repository_ids {
299 self.inner.write_string(id)?;
300 }
301 Ok(())
302 }
303
304 /// Schreibt einen Chunked-Value-Tag (CDR §15.3.4.2 + §15.3.4.3 —
305 /// "chunked encoding"). Wire-Tag = 0x7FFF_FF0A
306 /// (chunked-flag + multi-repo-id-flag, beide gesetzt).
307 ///
308 /// Nach dem Tag folgen:
309 /// 1. `i32 count` + `string id[N]` (multi-repo-id-list).
310 /// 2. Pro Chunk: `i32 chunk_size_bytes` + `octet chunk_data[..]`.
311 /// 3. Zum Schluss: `i32 end_tag = -<nesting_level>` (Spec
312 /// §15.3.4.3 negative-Level-Marker).
313 ///
314 /// Diese Funktion schreibt den Tag-Header + Repo-IDs; die
315 /// Caller-Layer schreibt jeden Chunk via [`Self::write_chunk`] und
316 /// schliesst den ValueType mit [`Self::write_chunked_end`].
317 ///
318 /// # Errors
319 /// Encode-Fehler oder leere Repo-IDs.
320 pub fn write_chunked_value_tag(
321 &mut self,
322 repository_ids: &[&str],
323 ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
324 debug_assert!(
325 !repository_ids.is_empty(),
326 "Spec §15.3.4.2: list-of-repository-ids must contain >=1 entry"
327 );
328 self.inner.write_u32(0x7FFF_FF0A)?;
329 let count: i32 = repository_ids.len() as i32;
330 self.inner.write_u32(count as u32)?;
331 for id in repository_ids {
332 self.inner.write_string(id)?;
333 }
334 Ok(())
335 }
336
337 /// Schreibt einen einzelnen Chunk innerhalb eines chunked-encoded
338 /// Value (CDR §15.3.4.3): `i32 chunk_size` + raw bytes.
339 ///
340 /// # Errors
341 /// Encode-Fehler.
342 pub fn write_chunk(
343 &mut self,
344 chunk_data: &[u8],
345 ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
346 let size: i32 = chunk_data.len() as i32;
347 self.inner.write_u32(size as u32)?;
348 for b in chunk_data {
349 self.inner.write_u8(*b)?;
350 }
351 Ok(())
352 }
353
354 /// Schreibt den End-Tag eines chunked-encoded Value (Spec
355 /// §15.3.4.3 — `i32 end_tag = -<nesting_level>` mit
356 /// `nesting_level >= 1` fuer den outermost Value).
357 ///
358 /// # Errors
359 /// Encode-Fehler oder `nesting_level == 0`.
360 pub fn write_chunked_end(
361 &mut self,
362 nesting_level: u32,
363 ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
364 debug_assert!(
365 nesting_level >= 1,
366 "Spec §15.3.4.3: chunked-end nesting_level must be >= 1"
367 );
368 let end_tag: i32 = -(nesting_level as i32);
369 self.inner.write_u32(end_tag as u32)?;
370 Ok(())
371 }
372}
373
374/// Stream-API zum Lesen von Valuetype-Wire-Bytes (CDR §15.3.4).
375pub struct ValueStreamReader<'a, 'b> {
376 /// Inner-Reader.
377 pub inner: &'a mut zerodds_cdr::BufferReader<'b>,
378}
379
380impl<'a, 'b> ValueStreamReader<'a, 'b> {
381 /// Konstruktor.
382 pub fn new(inner: &'a mut zerodds_cdr::BufferReader<'b>) -> Self {
383 Self { inner }
384 }
385
386 /// Liest einen value-tag und gibt die Repository-ID zurueck.
387 /// `Ok(None)` bei null-value (`0x00000000`-Tag).
388 ///
389 /// Rueckgabe ist `Some(first_repo_id)` — bei multi-repo-id-Listen
390 /// wird die erste ID geliefert; fuer den vollstaendigen Listen-
391 /// Roundtrip siehe [`Self::read_value_tag_full`].
392 ///
393 /// # Errors
394 /// Decode-Fehler (truncated, unbekannter Tag-Typ).
395 pub fn read_value_tag(
396 &mut self,
397 ) -> ::core::result::Result<Option<String>, zerodds_cdr::DecodeError> {
398 let header = self.read_value_tag_full()?;
399 Ok(match header {
400 ValueTagHeader::Null => None,
401 ValueTagHeader::Single(id) => Some(id),
402 ValueTagHeader::List(ids) => ids.into_iter().next(),
403 ValueTagHeader::ChunkedList(ids) => ids.into_iter().next(),
404 })
405 }
406
407 /// Liest einen value-tag mit voller Header-Information — single,
408 /// multi-repo-id-Liste oder chunked-Variante.
409 ///
410 /// # Errors
411 /// Decode-Fehler oder unbekannter Wire-Tag.
412 pub fn read_value_tag_full(
413 &mut self,
414 ) -> ::core::result::Result<ValueTagHeader, zerodds_cdr::DecodeError> {
415 let tag = self.inner.read_u32()?;
416 match tag {
417 0x0000_0000 => Ok(ValueTagHeader::Null),
418 0x7FFF_FF02 => {
419 let id = self.inner.read_string()?;
420 Ok(ValueTagHeader::Single(id))
421 }
422 0x7FFF_FF06 => {
423 let count = self.inner.read_u32()? as usize;
424 let mut ids = Vec::with_capacity(count);
425 for _ in 0..count {
426 ids.push(self.inner.read_string()?);
427 }
428 Ok(ValueTagHeader::List(ids))
429 }
430 0x7FFF_FF0A => {
431 let count = self.inner.read_u32()? as usize;
432 let mut ids = Vec::with_capacity(count);
433 for _ in 0..count {
434 ids.push(self.inner.read_string()?);
435 }
436 Ok(ValueTagHeader::ChunkedList(ids))
437 }
438 _ => Err(zerodds_cdr::DecodeError::InvalidString {
439 offset: 0,
440 reason: "ValueStream: unsupported value-tag (only 0/0x7FFFFF02/0x7FFFFF06/0x7FFFFF0A)",
441 }),
442 }
443 }
444
445 /// Liest einen Chunk innerhalb eines chunked-encoded Values.
446 /// Rueckgabe ist die Chunk-Groesse in Bytes (positiv) bzw. ein
447 /// negativer End-Tag (Spec §15.3.4.3 — `-nesting_level` markiert
448 /// das Ende des chunked-Values).
449 ///
450 /// # Errors
451 /// Decode-Fehler.
452 pub fn read_chunk_size(&mut self) -> ::core::result::Result<i32, zerodds_cdr::DecodeError> {
453 let raw = self.inner.read_u32()?;
454 Ok(raw as i32)
455 }
456}
457
458/// Spec §15.3.4.2 — Header-Variante eines value-tag.
459#[derive(Debug, Clone, PartialEq, Eq)]
460pub enum ValueTagHeader {
461 /// Null-Value-Tag (`0x00000000`).
462 Null,
463 /// Single-Repository-ID (`0x7FFFFF02`).
464 Single(String),
465 /// Liste von Repository-IDs (`0x7FFFFF06`) — Spec §15.3.4.2,
466 /// dient der Truncation-Kandidaten-Auflistung.
467 List(Vec<String>),
468 /// Chunked-Encoding mit Repository-ID-Liste (`0x7FFFFF0A`) —
469 /// Spec §15.3.4.3.
470 ChunkedList(Vec<String>),
471}
472
473// ============================================================================
474// §7.1 Component / Home — minimale CCM-Servant-Bindings (Phase-1).
475// ============================================================================
476
477/// CCM-Container-Servant-Marker. Concrete Component-Implementations
478/// impl-en das plus den `ComponentHome`-Trait.
479///
480/// Phase-2 erweitert mit Receptacle-/Facet-/Event-Bindings (CCM 4.0 §6).
481pub trait ComponentServant: Servant {
482 /// Liefert die Repository-ID der Component-Definition.
483 fn component_repository_id(&self) -> &str;
484}
485
486/// CCM-Home-Trait. End-User-Homes impl-en das plus die spezifische
487/// `create()`-Operation.
488pub trait ComponentHome {
489 /// Liefert die Repository-ID des Home.
490 fn home_repository_id(&self) -> &str;
491}
492
493// ============================================================================
494// §7.3 GIOP-Wire-Wiring — Connection-Stub.
495// ============================================================================
496
497/// Connection-Handle, das von Stubs zur GIOP-Request-Versendung
498/// verwendet wird. Phase-1 ist das ein Adapter-Trait, dessen volle
499/// Implementation in `corba-iiop` lebt.
500pub trait CorbaConnection {
501 /// Sendet einen GIOP-Request und blockiert bis Reply oder
502 /// System-Exception kommt.
503 ///
504 /// `target_ior` = Object-Reference, `operation` = Method-Name,
505 /// `request_payload` = bereits encoder Body (DataType-CDR).
506 ///
507 /// # Errors
508 /// Wire-Fehler oder Server-Side-Exception.
509 fn invoke(
510 &self,
511 target_ior: &ObjectReference,
512 operation: &str,
513 request_payload: &[u8],
514 ) -> Result<Vec<u8>, CorbaException>;
515
516 /// Sendet einen oneway-Request (kein Reply erwartet).
517 ///
518 /// # Errors
519 /// Wire-Fehler waehrend des Send.
520 fn invoke_oneway(
521 &self,
522 target_ior: &ObjectReference,
523 operation: &str,
524 request_payload: &[u8],
525 ) -> Result<(), CorbaException>;
526}