Skip to main content

zerodds_corba_rust/
runtime.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Runtime types that the generated code references.
4//!
5//! Phase 1 skeleton. The full wire implementation (GIOP marshalling +
6//! IIOP connection) follows in phase 2 via `corba-iiop` and
7//! `corba-giop`. Here live only the public API structures that the
8//! emitted stub/skeleton/valuetype references.
9
10extern crate alloc;
11use alloc::string::String;
12use alloc::vec::Vec;
13
14/// CORBA object reference (IOR-encoded). Generated stubs hold an
15/// instance of this struct as a connection handle.
16#[derive(Debug, Clone, PartialEq, Eq, Default)]
17pub struct ObjectReference {
18    /// Type identifier (Repository ID, e.g.
19    /// `IDL:omg.org/MyInterface:1.0`).
20    pub type_id: String,
21    /// IIOP profile encapsulation (CDR, one `TAG_INTERNET_IOP` profile body).
22    pub iiop_profile: Vec<u8>,
23}
24
25/// `TAG_INTERNET_IOP` — the only profile tag that ZeroDDS ObjectReferences
26/// carry (CORBA §13.6.3).
27const TAG_INTERNET_IOP: u32 = 0;
28
29/// Wire marshalling of an object reference as an **IOR** (CORBA §15.3.3):
30/// `struct IOR { string type_id; sequence<TaggedProfile> profiles; }` with
31/// `TaggedProfile { ProfileId tag; sequence<octet> profile_data; }`.
32/// `iiop_profile` is already the finished profile encapsulation, so it is
33/// inserted as `sequence<octet>` (length + bytes). This is the format
34/// in which omniORB/TAO/JacORB expect an `Object` reference on the wire.
35impl zerodds_cdr::CdrEncode for ObjectReference {
36    fn encode(
37        &self,
38        writer: &mut zerodds_cdr::BufferWriter,
39    ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
40        writer.write_string(&self.type_id)?;
41        writer.write_u32(1)?; // exactly one profile
42        writer.write_u32(TAG_INTERNET_IOP)?;
43        writer.write_u32(self.iiop_profile.len() as u32)?;
44        writer.write_bytes(&self.iiop_profile)?;
45        ::core::result::Result::Ok(())
46    }
47}
48
49impl zerodds_cdr::CdrDecode for ObjectReference {
50    fn decode(
51        reader: &mut zerodds_cdr::BufferReader<'_>,
52    ) -> ::core::result::Result<Self, zerodds_cdr::DecodeError> {
53        let type_id = reader.read_string()?;
54        let profile_count = reader.read_u32()?;
55        // Take the first TAG_INTERNET_IOP profile, skip the rest
56        // (multi-profile IORs are rare; the first IIOP profile suffices).
57        let mut iiop_profile = Vec::new();
58        for _ in 0..profile_count {
59            let tag = reader.read_u32()?;
60            let len = reader.read_u32()? as usize;
61            let data = reader.read_bytes(len)?;
62            if tag == TAG_INTERNET_IOP && iiop_profile.is_empty() {
63                iiop_profile = data.to_vec();
64            }
65        }
66        ::core::result::Result::Ok(Self {
67            type_id,
68            iiop_profile,
69        })
70    }
71}
72
73/// CORBA system/user exception. Generated stubs/skeletons map
74/// all error paths onto this variant.
75#[derive(Debug, Clone, PartialEq, Eq)]
76#[non_exhaustive]
77pub enum CorbaException {
78    /// CORBA system exception (Spec §3.17.1). Minor code per OMG.
79    SystemException {
80        /// Minor code from the spec standard (e.g. `0x4F4D000B` =
81        /// `BAD_OPERATION`).
82        minor: u32,
83        /// Static error description.
84        message: &'static str,
85    },
86    /// User exception from IDL `exception E { ... };` (§15.4.3 / §15.3.5.1).
87    ///
88    /// `body` is the **complete** UserException reply body as a
89    /// contiguous CDR stream: `string repository_id` **followed** by the
90    /// exception members. Members are NOT extracted separately — their
91    /// CDR alignment is relative to the body start (which is 8-aligned in
92    /// the GIOP reply); a typed decoder first reads the repo-id (positioning
93    /// the reader), then the members. `endianness` is the byte order of `body`
94    /// (= reply byte order), required for correct member decoding.
95    UserException {
96        /// Repository ID of the exception type (for type selection + display);
97        /// also appears at the start of `body`.
98        repository_id: String,
99        /// Complete exception body: `string repo_id` + members (CDR).
100        body: Vec<u8>,
101        /// On-wire byte order of `body`.
102        endianness: zerodds_cdr::Endianness,
103    },
104}
105
106impl ::core::fmt::Display for CorbaException {
107    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
108        match self {
109            Self::SystemException { minor, message } => {
110                write!(f, "CORBA::SystemException(minor={minor}): {message}")
111            }
112            Self::UserException { repository_id, .. } => {
113                write!(f, "CORBA::UserException({repository_id})")
114            }
115        }
116    }
117}
118
119impl ::std::error::Error for CorbaException {}
120
121/// ValueBase trait. All IDL valuetypes extend this.
122pub trait ValueBase {
123    /// Repository ID of the valuetype (e.g. `IDL:omg.org/MyValue:1.0`).
124    fn repository_id(&self) -> &str;
125}
126
127/// Result of a skeleton dispatch.
128#[derive(Debug, Clone, PartialEq, Eq)]
129#[non_exhaustive]
130pub enum SkeletonResult {
131    /// Operation succeeded, reply bytes are available.
132    Reply(Vec<u8>),
133    /// Operation threw a user or system exception.
134    Exception(CorbaException),
135    /// Unknown operation name for the interface — the POA
136    /// must return BAD_OPERATION.
137    BadOperation,
138    /// Operation is declared but wire marshalling is phase 2 —
139    /// placeholder for now.
140    NotYetWired,
141}
142
143/// POA servant marker. Concrete server implementations implement this
144/// plus the respective IDL interface trait.
145pub trait Servant {
146    /// Repository ID of the interface that the servant implements.
147    fn target_repository_id(&self) -> &str;
148}
149
150// ============================================================================
151// §2.4 TypeCode (CORBA 3.3 §10.7) — minimally functional wrapper.
152// ============================================================================
153
154/// CORBA `TypeCode` — type-reflection wrapper.
155///
156/// Full TypeCode operations (kind, member_count, member_name,
157/// content_type, ...) are evaluated via the `corba-iiop` IIOP connection —
158/// phase 1 here is a wrapper around the Repository ID,
159/// phase 2 extends it with a lookup API.
160#[derive(Debug, Clone, PartialEq, Eq, Default)]
161pub struct TypeCode {
162    /// Repository ID of the referenced type.
163    pub repository_id: String,
164}
165
166impl TypeCode {
167    /// Constructs a TypeCode from a Repository ID.
168    #[must_use]
169    pub fn new(repository_id: impl Into<String>) -> Self {
170        Self {
171            repository_id: repository_id.into(),
172        }
173    }
174}
175
176// ============================================================================
177// §7.2 POA configuration builder (Spec §11.3 — 7 policies)
178// ============================================================================
179
180/// CORBA POA builder for server-side object activation.
181///
182/// Spec §11.3 defines 7 policies; all have sensible defaults
183/// for typical embedded/server use cases. The end user
184/// overrides selectively via `with_*` methods and calls `build()`
185/// for the final `PoaConfiguration`.
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct PoaBuilder {
188    /// Threading policy (Spec §11.3.4).
189    pub thread_policy: ThreadPolicy,
190    /// Lifespan policy (§11.3.5).
191    pub lifespan_policy: LifespanPolicy,
192    /// IdAssignment policy (§11.3.6).
193    pub id_assignment_policy: IdAssignmentPolicy,
194    /// IdUniqueness policy (§11.3.7).
195    pub id_uniqueness_policy: IdUniquenessPolicy,
196    /// ImplicitActivation policy (§11.3.8).
197    pub implicit_activation_policy: ImplicitActivationPolicy,
198    /// ServantRetention policy (§11.3.9).
199    pub servant_retention_policy: ServantRetentionPolicy,
200    /// RequestProcessing policy (§11.3.10).
201    pub request_processing_policy: RequestProcessingPolicy,
202}
203
204impl Default for PoaBuilder {
205    fn default() -> Self {
206        Self::new()
207    }
208}
209
210impl PoaBuilder {
211    /// Creates a new POA builder with spec default policies.
212    #[must_use]
213    pub fn new() -> Self {
214        Self {
215            thread_policy: ThreadPolicy::OrbCtrlModel,
216            lifespan_policy: LifespanPolicy::Transient,
217            id_assignment_policy: IdAssignmentPolicy::SystemId,
218            id_uniqueness_policy: IdUniquenessPolicy::Unique,
219            implicit_activation_policy: ImplicitActivationPolicy::NoImplicitActivation,
220            servant_retention_policy: ServantRetentionPolicy::Retain,
221            request_processing_policy: RequestProcessingPolicy::UseActiveObjectMapOnly,
222        }
223    }
224}
225
226/// POA threading policy (Spec §11.3.4).
227#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228pub enum ThreadPolicy {
229    /// Default: ORB chooses threading.
230    OrbCtrlModel,
231    /// Single-threaded.
232    SingleThreadModel,
233    /// Main-thread-only.
234    MainThreadModel,
235}
236
237/// POA lifespan policy (Spec §11.3.5).
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub enum LifespanPolicy {
240    /// Default: object refs are valid only for the ORB lifetime.
241    Transient,
242    /// Object refs survive an ORB restart.
243    Persistent,
244}
245
246/// POA IdAssignment policy (§11.3.6).
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub enum IdAssignmentPolicy {
249    /// Default: ORB assigns object IDs.
250    SystemId,
251    /// Application assigns object IDs.
252    UserId,
253}
254
255/// POA IdUniqueness policy (§11.3.7).
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum IdUniquenessPolicy {
258    /// Default: each servant has exactly one object ID.
259    Unique,
260    /// A servant may have multiple object IDs.
261    Multiple,
262}
263
264/// POA ImplicitActivation policy (§11.3.8).
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
266pub enum ImplicitActivationPolicy {
267    /// Default: explicit `activate_object` required.
268    NoImplicitActivation,
269    /// Auto-activation on the first method call.
270    ImplicitActivation,
271}
272
273/// POA ServantRetention policy (§11.3.9).
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275pub enum ServantRetentionPolicy {
276    /// Default: ActiveObjectMap holds servants.
277    Retain,
278    /// Stateless — a fresh servant per request.
279    NonRetain,
280}
281
282/// POA RequestProcessing policy (§11.3.10).
283#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284pub enum RequestProcessingPolicy {
285    /// Default: ActiveObjectMap lookup, otherwise BAD_OPERATION.
286    UseActiveObjectMapOnly,
287    /// Lookup, then ServantManager fallback.
288    UseDefaultServant,
289    /// Lookup, then ServantActivator/Locator.
290    UseServantManager,
291}
292
293// ============================================================================
294// §4.4 Valuetype wire marshalling — minimal stream API
295// ============================================================================
296
297/// Stream API for ValueBase wire marshalling (CDR §15.3.4).
298///
299/// Phase 1: API skeleton that wraps `zerodds_cdr::BufferReader/Writer` and
300/// applies the value-tag logic (chunked encoding, repository-id list).
301/// Phase 2 wires the full spec conformance (truncatable, custom
302/// marshaling, codeset).
303pub struct ValueStreamWriter<'a> {
304    /// Inner writer.
305    pub inner: &'a mut zerodds_cdr::BufferWriter,
306}
307
308impl<'a> ValueStreamWriter<'a> {
309    /// Constructor.
310    pub fn new(inner: &'a mut zerodds_cdr::BufferWriter) -> Self {
311        Self { inner }
312    }
313
314    /// Writes a value-tag (CDR §15.3.4.2). Format: 0x7FFFFF02
315    /// for single-repository-id, 0x7FFFFF06 for list-of-repository-
316    /// ids, 0x00000000 for null-value.
317    ///
318    /// # Errors
319    /// Encode error.
320    pub fn write_value_tag(
321        &mut self,
322        repository_id: &str,
323    ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
324        // Single-repository-id tag (chunked = false, codeset = false).
325        self.inner.write_u32(0x7FFF_FF02)?;
326        self.inner.write_string(repository_id)?;
327        Ok(())
328    }
329
330    /// Writes a value-tag with a multi-repository-id list (CDR
331    /// §15.3.4.2). Wire tag = 0x7FFF_FF06 (`list-of-repository-ids` +
332    /// no chunked bit). Layout:
333    ///
334    /// ```text
335    /// u32 tag = 0x7FFFFF06
336    /// i32 count                 // > 0
337    /// string id[0] .. id[N-1]   // CDR-strings (length + bytes + NUL)
338    /// ```
339    ///
340    /// # Errors
341    /// Encode error or empty `repository_ids` (spec requires N >= 1).
342    pub fn write_value_tag_multi(
343        &mut self,
344        repository_ids: &[&str],
345    ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
346        debug_assert!(
347            !repository_ids.is_empty(),
348            "Spec §15.3.4.2: list-of-repository-ids must contain >=1 entry"
349        );
350        self.inner.write_u32(0x7FFF_FF06)?;
351        // count as signed long.
352        let count: i32 = repository_ids.len() as i32;
353        self.inner.write_u32(count as u32)?;
354        for id in repository_ids {
355            self.inner.write_string(id)?;
356        }
357        Ok(())
358    }
359
360    /// Writes a chunked value-tag (CDR §15.3.4.2 + §15.3.4.3 —
361    /// "chunked encoding"). Wire tag = 0x7FFF_FF0A
362    /// (chunked flag + multi-repo-id flag, both set).
363    ///
364    /// After the tag come:
365    /// 1. `i32 count` + `string id[N]` (multi-repo-id-list).
366    /// 2. Per chunk: `i32 chunk_size_bytes` + `octet chunk_data[..]`.
367    /// 3. Finally: `i32 end_tag = -<nesting_level>` (Spec
368    ///    §15.3.4.3 negative-level marker).
369    ///
370    /// This function writes the tag header + repo IDs; the
371    /// caller layer writes each chunk via [`Self::write_chunk`] and
372    /// closes the ValueType with [`Self::write_chunked_end`].
373    ///
374    /// # Errors
375    /// Encode error or empty repo IDs.
376    pub fn write_chunked_value_tag(
377        &mut self,
378        repository_ids: &[&str],
379    ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
380        debug_assert!(
381            !repository_ids.is_empty(),
382            "Spec §15.3.4.2: list-of-repository-ids must contain >=1 entry"
383        );
384        self.inner.write_u32(0x7FFF_FF0A)?;
385        let count: i32 = repository_ids.len() as i32;
386        self.inner.write_u32(count as u32)?;
387        for id in repository_ids {
388            self.inner.write_string(id)?;
389        }
390        Ok(())
391    }
392
393    /// Writes a single chunk within a chunked-encoded
394    /// value (CDR §15.3.4.3): `i32 chunk_size` + raw bytes.
395    ///
396    /// # Errors
397    /// Encode error.
398    pub fn write_chunk(
399        &mut self,
400        chunk_data: &[u8],
401    ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
402        let size: i32 = chunk_data.len() as i32;
403        self.inner.write_u32(size as u32)?;
404        for b in chunk_data {
405            self.inner.write_u8(*b)?;
406        }
407        Ok(())
408    }
409
410    /// Writes the end-tag of a chunked-encoded value (Spec
411    /// §15.3.4.3 — `i32 end_tag = -<nesting_level>` with
412    /// `nesting_level >= 1` for the outermost value).
413    ///
414    /// # Errors
415    /// Encode error or `nesting_level == 0`.
416    pub fn write_chunked_end(
417        &mut self,
418        nesting_level: u32,
419    ) -> ::core::result::Result<(), zerodds_cdr::EncodeError> {
420        debug_assert!(
421            nesting_level >= 1,
422            "Spec §15.3.4.3: chunked-end nesting_level must be >= 1"
423        );
424        let end_tag: i32 = -(nesting_level as i32);
425        self.inner.write_u32(end_tag as u32)?;
426        Ok(())
427    }
428}
429
430/// Stream API for reading valuetype wire bytes (CDR §15.3.4).
431pub struct ValueStreamReader<'a, 'b> {
432    /// Inner reader.
433    pub inner: &'a mut zerodds_cdr::BufferReader<'b>,
434}
435
436impl<'a, 'b> ValueStreamReader<'a, 'b> {
437    /// Constructor.
438    pub fn new(inner: &'a mut zerodds_cdr::BufferReader<'b>) -> Self {
439        Self { inner }
440    }
441
442    /// Reads a value-tag and returns the Repository ID.
443    /// `Ok(None)` for null-value (`0x00000000` tag).
444    ///
445    /// The return is `Some(first_repo_id)` — for multi-repo-id lists
446    /// the first ID is delivered; for the full list
447    /// roundtrip see [`Self::read_value_tag_full`].
448    ///
449    /// # Errors
450    /// Decode error (truncated, unknown tag type).
451    pub fn read_value_tag(
452        &mut self,
453    ) -> ::core::result::Result<Option<String>, zerodds_cdr::DecodeError> {
454        let header = self.read_value_tag_full()?;
455        Ok(match header {
456            ValueTagHeader::Null => None,
457            ValueTagHeader::Single(id) => Some(id),
458            ValueTagHeader::List(ids) => ids.into_iter().next(),
459            ValueTagHeader::ChunkedList(ids) => ids.into_iter().next(),
460        })
461    }
462
463    /// Reads a value-tag with full header information — single,
464    /// multi-repo-id list, or chunked variant.
465    ///
466    /// # Errors
467    /// Decode error or unknown wire tag.
468    pub fn read_value_tag_full(
469        &mut self,
470    ) -> ::core::result::Result<ValueTagHeader, zerodds_cdr::DecodeError> {
471        let tag = self.inner.read_u32()?;
472        match tag {
473            0x0000_0000 => Ok(ValueTagHeader::Null),
474            0x7FFF_FF02 => {
475                let id = self.inner.read_string()?;
476                Ok(ValueTagHeader::Single(id))
477            }
478            0x7FFF_FF06 => {
479                let count = self.inner.read_u32()? as usize;
480                let mut ids = Vec::with_capacity(count);
481                for _ in 0..count {
482                    ids.push(self.inner.read_string()?);
483                }
484                Ok(ValueTagHeader::List(ids))
485            }
486            0x7FFF_FF0A => {
487                let count = self.inner.read_u32()? as usize;
488                let mut ids = Vec::with_capacity(count);
489                for _ in 0..count {
490                    ids.push(self.inner.read_string()?);
491                }
492                Ok(ValueTagHeader::ChunkedList(ids))
493            }
494            _ => Err(zerodds_cdr::DecodeError::InvalidString {
495                offset: 0,
496                reason: "ValueStream: unsupported value-tag (only 0/0x7FFFFF02/0x7FFFFF06/0x7FFFFF0A)",
497            }),
498        }
499    }
500
501    /// Reads a chunk within a chunked-encoded value.
502    /// The return is the chunk size in bytes (positive) or a
503    /// negative end-tag (Spec §15.3.4.3 — `-nesting_level` marks
504    /// the end of the chunked value).
505    ///
506    /// # Errors
507    /// Decode error.
508    pub fn read_chunk_size(&mut self) -> ::core::result::Result<i32, zerodds_cdr::DecodeError> {
509        let raw = self.inner.read_u32()?;
510        Ok(raw as i32)
511    }
512}
513
514/// Spec §15.3.4.2 — header variant of a value-tag.
515#[derive(Debug, Clone, PartialEq, Eq)]
516pub enum ValueTagHeader {
517    /// Null-value tag (`0x00000000`).
518    Null,
519    /// Single Repository ID (`0x7FFFFF02`).
520    Single(String),
521    /// List of Repository IDs (`0x7FFFFF06`) — Spec §15.3.4.2,
522    /// used to enumerate truncation candidates.
523    List(Vec<String>),
524    /// Chunked encoding with a Repository ID list (`0x7FFFFF0A`) —
525    /// Spec §15.3.4.3.
526    ChunkedList(Vec<String>),
527}
528
529// ============================================================================
530// §7.1 Component / Home — minimal CCM servant bindings (phase 1).
531// ============================================================================
532
533/// CCM container servant marker. Concrete component implementations
534/// implement this plus the `ComponentHome` trait.
535///
536/// Phase 2 extends it with receptacle/facet/event bindings (CCM 4.0 §6).
537pub trait ComponentServant: Servant {
538    /// Returns the Repository ID of the component definition.
539    fn component_repository_id(&self) -> &str;
540}
541
542/// CCM home trait. End-user homes implement this plus the specific
543/// `create()` operation.
544pub trait ComponentHome {
545    /// Returns the Repository ID of the home.
546    fn home_repository_id(&self) -> &str;
547}
548
549// ============================================================================
550// §7.3 GIOP wire wiring — connection stub.
551// ============================================================================
552
553/// Connection handle used by stubs to send GIOP requests.
554/// Phase 1 is an adapter trait whose full implementation lives in
555/// `corba-iiop`.
556pub trait CorbaConnection {
557    /// Sends a GIOP request and blocks until a reply or
558    /// system exception arrives.
559    ///
560    /// `target_ior` = object reference, `operation` = method name,
561    /// `request_endianness` = on-wire byte order in which `request_payload`
562    /// is CDR-encoded (set into the GIOP flag), `request_payload` =
563    /// already-encoded body (classic CDR of the in-params).
564    ///
565    /// Returns the reply body **plus its** on-wire byte order — the
566    /// server may reply in its native order (CDR "receiver makes it
567    /// right"), so the stub must read the reply in the returned order.
568    ///
569    /// # Errors
570    /// Wire error or server-side exception.
571    fn invoke(
572        &self,
573        target_ior: &ObjectReference,
574        operation: &str,
575        request_endianness: zerodds_cdr::Endianness,
576        request_payload: &[u8],
577    ) -> Result<(Vec<u8>, zerodds_cdr::Endianness), CorbaException>;
578
579    /// Sends a oneway request (no reply expected).
580    ///
581    /// # Errors
582    /// Wire error during the send.
583    fn invoke_oneway(
584        &self,
585        target_ior: &ObjectReference,
586        operation: &str,
587        request_endianness: zerodds_cdr::Endianness,
588        request_payload: &[u8],
589    ) -> Result<(), CorbaException>;
590}
591
592/// Reply outcome of an asynchronous invocation (CORBA Messaging §22): the raw
593/// reply body + its on-wire byte order on success, otherwise the CORBA exception.
594pub type AsyncReply = Result<(Vec<u8>, zerodds_cdr::Endianness), CorbaException>;
595
596/// Callback for the AMI callback model (§22.5) — invoked exactly once.
597pub type AsyncReplyCallback = alloc::boxed::Box<dyn FnOnce(AsyncReply) + Send>;
598
599/// Abstract **asynchronous** GIOP channel to ONE target object (CORBA Messaging
600/// §22) used by the generated AMI stubs (`sendc_`/`sendp_`). Implemented
601/// by the transport layer (`zerodds-corba-interop::AmiClient`) — same
602/// layering pattern as [`CorbaConnection`] for the synchronous side.
603///
604/// The stubs marshal the in-args into `payload` (big-endian) and decode the
605/// reply in a typed way; this channel carries only opaque bytes + the
606/// `request_id` demux.
607pub trait AsyncCorbaChannel {
608    /// **Callback model** (§22.5): fires `operation(payload)` and registers
609    /// `cb` for the reply; returns the `request_id`.
610    ///
611    /// # Errors
612    /// Transport error during sending.
613    fn send(
614        &mut self,
615        operation: &str,
616        payload: &[u8],
617        cb: AsyncReplyCallback,
618    ) -> Result<u32, CorbaException>;
619
620    /// **Polling model** (§22.6): fires `operation(payload)`, returns the
621    /// `request_id`; the reply is fetched via [`get_reply`](Self::get_reply).
622    ///
623    /// # Errors
624    /// Transport error during sending.
625    fn send_poll(&mut self, operation: &str, payload: &[u8]) -> Result<u32, CorbaException>;
626
627    /// Drives the channel until the reply for `request_id` (from
628    /// [`send_poll`](Self::send_poll)) is available, and returns it.
629    ///
630    /// # Errors
631    /// Transport error, or `request_id` is not open.
632    fn get_reply(&mut self, request_id: u32) -> Result<AsyncReply, CorbaException>;
633
634    /// Reads EXACTLY one arrived reply (blocking) and dispatches it
635    /// (callback invocation or storage); returns the handled `request_id`.
636    ///
637    /// # Errors
638    /// Transport error, or nothing is open.
639    fn perform_work(&mut self) -> Result<u32, CorbaException>;
640
641    /// Drives [`perform_work`](Self::perform_work) until all callback requests
642    /// are processed.
643    ///
644    /// # Errors
645    /// Transport error.
646    fn perform_all(&mut self) -> Result<(), CorbaException>;
647}
648
649#[cfg(test)]
650#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
651mod object_reference_tests {
652    use super::*;
653    use zerodds_cdr::{BufferReader, BufferWriter, CdrDecode, CdrEncode, Endianness};
654
655    #[test]
656    fn object_reference_roundtrips_as_ior() {
657        let obj = ObjectReference {
658            type_id: "IDL:demo/Bench:1.0".into(),
659            iiop_profile: alloc::vec![0x01, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef],
660        };
661        let mut w = BufferWriter::new(Endianness::Big);
662        obj.encode(&mut w).unwrap();
663        let bytes = w.into_bytes();
664
665        // Wire structure (CORBA §15.3.3 IOR): string type_id + seq<TaggedProfile>.
666        let mut r = BufferReader::new(&bytes, Endianness::Big);
667        assert_eq!(r.read_string().unwrap(), "IDL:demo/Bench:1.0");
668        assert_eq!(r.read_u32().unwrap(), 1, "exactly one profile");
669        assert_eq!(r.read_u32().unwrap(), 0, "TAG_INTERNET_IOP");
670        assert_eq!(r.read_u32().unwrap(), 8, "profile_data length");
671
672        // Full roundtrip.
673        let mut r2 = BufferReader::new(&bytes, Endianness::Big);
674        let decoded = ObjectReference::decode(&mut r2).unwrap();
675        assert_eq!(decoded, obj);
676    }
677
678    #[test]
679    fn object_reference_little_endian_roundtrips() {
680        let obj = ObjectReference {
681            type_id: "IDL:X:1.0".into(),
682            iiop_profile: alloc::vec![1, 2, 3],
683        };
684        let mut w = BufferWriter::new(Endianness::Little);
685        obj.encode(&mut w).unwrap();
686        let bytes = w.into_bytes();
687        let mut r = BufferReader::new(&bytes, Endianness::Little);
688        assert_eq!(ObjectReference::decode(&mut r).unwrap(), obj);
689    }
690}