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}