zerodds_ami4ccm/exception_holder.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! `CCM_AMI::ExceptionHolder` Modell — Spec §7.4.1.
5//!
6//! Spec-IDL (Annex A — `ami4ccm.idl`):
7//! ```idl
8//! module CCM_AMI {
9//! native UserExceptionBase;
10//! local interface ExceptionHolder {
11//! void raise_exception() raises (UserExceptionBase);
12//! };
13//! };
14//! ```
15
16use alloc::string::String;
17use alloc::vec::Vec;
18
19/// `CCM_AMI::UserExceptionBase` (Spec §7.4.1, S. 9).
20///
21/// Spec-Zitat: "Language mapping of this native type should allow any
22/// user exception to be raised from this method. For instance, it is
23/// mapped to CORBA::UserException in C++ and to org.omg.CORBA.
24/// UserException in java."
25///
26/// In ZeroDDS modellieren wir die `native`-Form als (Repository-ID,
27/// marshaled-Bytes)-Paar. Das ist ausreichend, um eine User-Exception
28/// von Server-Side serialisiert und im Client wieder rekonstruiert zu
29/// erhalten — Sprach-Mapping (CORBA::UserException, java.lang.Throwable,
30/// etc.) erfolgt beim Codegen.
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct UserExceptionBase {
33 /// CORBA Repository-ID der originalen Exception
34 /// (z.B. `IDL:Stock/InvalidStock:1.0`).
35 pub repository_id: String,
36 /// CDR-marshaled Member-State der Exception.
37 pub marshaled_exception: Vec<u8>,
38}
39
40impl UserExceptionBase {
41 /// Konstruiert eine `UserExceptionBase` aus Repository-ID + Bytes.
42 #[must_use]
43 pub fn new(repository_id: impl Into<String>, marshaled_exception: Vec<u8>) -> Self {
44 Self {
45 repository_id: repository_id.into(),
46 marshaled_exception,
47 }
48 }
49}
50
51/// `CCM_AMI::ExceptionHolder`-Instanz (Spec §7.4.1, S. 9).
52///
53/// Spec-Zitat: "The CCM_AMI::ExceptionHolder interface encapsulates the
54/// exception data and enough information to turn that data back into a
55/// raised exception."
56///
57/// Operation `raise_exception()` (Spec §7.4.1) rekonstruiert die
58/// Exception aus dem Holder. In ZeroDDS modellieren wir das als
59/// `Result<(), UserExceptionBase>` — `Err(...)` ist der "raise"-Pfad.
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct ExceptionHolder {
62 inner: UserExceptionBase,
63}
64
65impl ExceptionHolder {
66 /// Konstruiert `ExceptionHolder` aus `UserExceptionBase`.
67 #[must_use]
68 pub fn new(exception: UserExceptionBase) -> Self {
69 Self { inner: exception }
70 }
71
72 /// Spec §7.4.1 — `raise_exception()`. Liefert die enthaltene
73 /// Exception als `Err`-Variante. Da Exceptions nicht auf
74 /// `core::error::Error`-Trait gemappt werden (das ist Sprach-spezifisch),
75 /// ist der Rueckgabe-Typ `Err(UserExceptionBase)`.
76 ///
77 /// # Errors
78 /// Diese Operation gibt **immer** `Err` zurueck — der Holder
79 /// existiert genau, um eine Exception zu transportieren. Der `Ok(())`-
80 /// Pfad ist nur formal, um den Spec-Signature-Style abzubilden.
81 pub fn raise_exception(&self) -> Result<(), UserExceptionBase> {
82 Err(self.inner.clone())
83 }
84
85 /// Read-Only-Zugriff auf die enthaltene Exception (z.B. fuer
86 /// Diagnostik, ohne sie zu "raisen").
87 #[must_use]
88 pub const fn user_exception(&self) -> &UserExceptionBase {
89 &self.inner
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn user_exception_base_new_stores_id_and_bytes() {
99 let ub = UserExceptionBase::new("IDL:Stock/InvalidStock:1.0", alloc::vec![0xDE, 0xAD]);
100 assert_eq!(ub.repository_id, "IDL:Stock/InvalidStock:1.0");
101 assert_eq!(ub.marshaled_exception, alloc::vec![0xDE, 0xAD]);
102 }
103
104 #[test]
105 fn raise_exception_returns_err_with_user_exception() {
106 // Spec §7.4.1 — raise_exception() raises UserExceptionBase.
107 let ub = UserExceptionBase::new("IDL:Foo:1.0", alloc::vec![1, 2, 3]);
108 let holder = ExceptionHolder::new(ub.clone());
109 let result = holder.raise_exception();
110 assert_eq!(result, Err(ub));
111 }
112
113 #[test]
114 fn user_exception_accessor_returns_inner() {
115 let ub = UserExceptionBase::new("IDL:Bar:1.0", alloc::vec![]);
116 let holder = ExceptionHolder::new(ub.clone());
117 assert_eq!(holder.user_exception(), &ub);
118 }
119}