Skip to main content

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}