Skip to main content

zerodds_corba_codegen/
skeleton.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! Skeleton-Code-Generation-Helper — Server-Side-Dispatch.
5//!
6//! Ein **Skeleton** dispatched einen GIOP-Request anhand des
7//! Operation-Names auf die konkrete Servant-Methode und marshallt
8//! die Reply.
9
10use alloc::string::String;
11
12use crate::special_types::TargetLanguage;
13
14/// Eine Operation, die das Skeleton dispatchen muss.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct SkeletonOp {
17    /// Operation-Name (Wire-Form).
18    pub name: String,
19    /// Servant-Method-Name (Sprach-Form, z.B. `getStock` in Java).
20    pub method_name: String,
21}
22
23/// Rendert einen Operation-Name-Switch fuer das Server-Skeleton.
24///
25/// Das ist ein einfaches `switch operation_name`-Konstrukt, das
26/// das Codegen in C++ / C# / Java emittiert. Der echte Method-Body
27/// (Marshalling) ist Sprach-spezifisch und liegt in den
28/// `crates/idl-*`-Crates; hier ist der Switch-Body als Helper.
29#[must_use]
30pub fn render_skeleton_dispatch(
31    class_name: &str,
32    ops: &[SkeletonOp],
33    lang: TargetLanguage,
34) -> String {
35    match lang {
36        TargetLanguage::Cpp => render_cpp(class_name, ops),
37        TargetLanguage::CSharp => render_csharp(class_name, ops),
38        TargetLanguage::Java => render_java(class_name, ops),
39        TargetLanguage::Rust => render_rust(class_name, ops),
40    }
41}
42
43fn render_rust(class_name: &str, ops: &[SkeletonOp]) -> String {
44    let mut s = alloc::format!(
45        "// Rust skeleton dispatch for {class_name} (used as template by zerodds-corba-rust)\nmatch op {{\n"
46    );
47    for op in ops {
48        s.push_str(&alloc::format!(
49            "    \"{}\" => self.{}(req),\n",
50            op.name,
51            op.method_name,
52        ));
53    }
54    s.push_str("    _ => Err(CorbaException::BadOperation),\n}\n");
55    s
56}
57
58fn render_cpp(class_name: &str, ops: &[SkeletonOp]) -> String {
59    let mut s = alloc::format!("// C++ skeleton dispatch for {class_name}\n");
60    s.push_str("if (false) {}\n");
61    for op in ops {
62        s.push_str(&alloc::format!(
63            "else if (strcmp(op, \"{}\") == 0) return this->{}(req);\n",
64            op.name,
65            op.method_name,
66        ));
67    }
68    s.push_str("else throw CORBA::BAD_OPERATION();\n");
69    s
70}
71
72fn render_csharp(class_name: &str, ops: &[SkeletonOp]) -> String {
73    let mut s = alloc::format!("// C# skeleton dispatch for {class_name}\nswitch (op) {{\n");
74    for op in ops {
75        s.push_str(&alloc::format!(
76            "    case \"{}\": return this.{}(req);\n",
77            op.name,
78            op.method_name,
79        ));
80    }
81    s.push_str("    default: throw new omg.org.CORBA.BAD_OPERATION();\n}\n");
82    s
83}
84
85fn render_java(class_name: &str, ops: &[SkeletonOp]) -> String {
86    let mut s = alloc::format!("// Java skeleton dispatch for {class_name}\nswitch (op) {{\n");
87    for op in ops {
88        s.push_str(&alloc::format!(
89            "    case \"{}\": return this.{}(req);\n",
90            op.name,
91            op.method_name,
92        ));
93    }
94    s.push_str("    default: throw new org.omg.CORBA.BAD_OPERATION();\n}\n");
95    s
96}
97
98#[cfg(test)]
99#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
100mod tests {
101    use super::*;
102
103    fn ops() -> alloc::vec::Vec<SkeletonOp> {
104        alloc::vec![
105            SkeletonOp {
106                name: "echo".into(),
107                method_name: "echo".into(),
108            },
109            SkeletonOp {
110                name: "ping".into(),
111                method_name: "Ping".into(),
112            },
113        ]
114    }
115
116    #[test]
117    fn cpp_dispatch_uses_strcmp_and_bad_operation() {
118        let s = render_skeleton_dispatch("EchoImpl", &ops(), TargetLanguage::Cpp);
119        assert!(s.contains("strcmp(op, \"echo\")"));
120        assert!(s.contains("CORBA::BAD_OPERATION"));
121    }
122
123    #[test]
124    fn csharp_dispatch_uses_switch_and_omg_org_namespace() {
125        let s = render_skeleton_dispatch("EchoImpl", &ops(), TargetLanguage::CSharp);
126        assert!(s.contains("switch (op)"));
127        assert!(s.contains("omg.org.CORBA.BAD_OPERATION"));
128    }
129
130    #[test]
131    fn java_dispatch_uses_org_omg_namespace() {
132        let s = render_skeleton_dispatch("EchoImpl", &ops(), TargetLanguage::Java);
133        assert!(s.contains("org.omg.CORBA.BAD_OPERATION"));
134    }
135}