Skip to main content

zerodds_corba_dds_bridge/
wire.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! Wire-Helpers — bringen die Bridge an konkrete CORBA-Wire-Crates
5//! (`zerodds-corba-giop` + `zerodds-corba-ior`) heran.
6//!
7//! Die Bridge selbst lebt logisch oberhalb der Wire-Crates und mappt
8//! Operation-Bytes auf DDS-Topics, aber Hosting braucht die Helpers
9//! um:
10//!
11//! 1. **GIOP-Requests zu inspizieren** (Operation-Name + Body
12//!    extrahieren ohne den ganzen Servant-Loop), und
13//! 2. **`object_key` aus einer IOR zu ziehen** — fuer den Lookup,
14//!    welche `BridgeRoute` einen eingehenden Request bedient.
15
16use alloc::string::String;
17use alloc::vec::Vec;
18
19use zerodds_corba_giop::{Message, decode_message};
20use zerodds_corba_ior::{Ior, ProfileId};
21
22/// Zusammenfassung einer GIOP-Request, wie sie der Bridge-Servant
23/// fuer das DDS-Mapping benoetigt.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct RequestSummary {
26    /// `request_id` aus dem GIOP-Header (bzw. der `RequestHeader`).
27    pub request_id: u32,
28    /// Operation-Name (vollqualifiziert).
29    pub operation: String,
30    /// Body-Bytes (das, was die Bridge auf das Topic publiziert).
31    pub body: Vec<u8>,
32}
33
34/// Decodiert ein GIOP-Frame (Header + Body) und extrahiert
35/// `request_id`/`operation`/`body` falls die Message ein
36/// `Request`-Type ist. Andere Message-Types werden nicht in eine
37/// `RequestSummary` gekapselt.
38#[must_use]
39pub fn decode_giop_request_bytes(frame: &[u8]) -> Option<RequestSummary> {
40    let (msg, body) = decode_message(frame).ok()?;
41    match msg {
42        Message::Request(r) => Some(RequestSummary {
43            request_id: r.request_id,
44            operation: r.operation,
45            body: body.to_vec(),
46        }),
47        _ => None,
48    }
49}
50
51/// Extrahiert das `object_key` aus dem ersten `TAG_INTERNET_IOP`-
52/// Profile einer IOR. Rueckgabe ist die fuer Bridge-Routes-Match
53/// benoetigte `Vec<u8>`-Form. Liefert `None` falls die IOR kein
54/// IIOP-Profile enthaelt oder die Decapsulation fehlschlaegt.
55#[must_use]
56pub fn object_key_from_ior(ior: &Ior) -> Option<Vec<u8>> {
57    for prof in &ior.profiles {
58        if prof.tag == ProfileId::InternetIop {
59            if let Some(Ok(body)) = prof.as_iiop() {
60                return Some(body.object_key);
61            }
62        }
63    }
64    None
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn decode_giop_request_bytes_rejects_non_request_frame() {
73        // 12-Byte-Header eines bekannt-ungueltigen Frames.
74        let bogus = [0u8; 12];
75        assert!(decode_giop_request_bytes(&bogus).is_none());
76    }
77
78    #[test]
79    fn object_key_from_empty_ior_is_none() {
80        let ior = Ior::default();
81        assert_eq!(object_key_from_ior(&ior), None);
82    }
83}