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//! A **skeleton** dispatches a GIOP request to the concrete servant
7//! method based on the operation name and marshals the reply.
8
9use alloc::string::String;
10
11use crate::special_types::TargetLanguage;
12
13/// An operation that the skeleton must dispatch.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct SkeletonOp {
16    /// Operation name (wire form).
17    pub name: String,
18    /// Servant method name (language form, e.g. `getStock` in Java).
19    pub method_name: String,
20}
21
22/// Renders an operation-name switch for the server skeleton.
23///
24/// This is a simple `switch operation_name` construct that the codegen
25/// emits in C++ / C# / Java. The actual method body (marshalling) is
26/// language-specific and lives in the `crates/idl-*` crates; here we
27/// only provide the switch body as a helper.
28#[must_use]
29pub fn render_skeleton_dispatch(
30    class_name: &str,
31    ops: &[SkeletonOp],
32    lang: TargetLanguage,
33) -> String {
34    match lang {
35        TargetLanguage::Cpp => render_cpp(class_name, ops),
36        TargetLanguage::CSharp => render_csharp(class_name, ops),
37        TargetLanguage::Java => render_java(class_name, ops),
38        TargetLanguage::Rust => render_rust(class_name, ops),
39    }
40}
41
42fn render_rust(class_name: &str, ops: &[SkeletonOp]) -> String {
43    let mut s = alloc::format!(
44        "// Rust skeleton dispatch for {class_name} (used as template by zerodds-corba-rust)\nmatch op {{\n"
45    );
46    for op in ops {
47        s.push_str(&alloc::format!(
48            "    \"{}\" => self.{}(req),\n",
49            op.name,
50            op.method_name,
51        ));
52    }
53    s.push_str("    _ => Err(CorbaException::BadOperation),\n}\n");
54    s
55}
56
57fn render_cpp(class_name: &str, ops: &[SkeletonOp]) -> String {
58    let mut s = alloc::format!("// C++ skeleton dispatch for {class_name}\n");
59    s.push_str("if (false) {}\n");
60    for op in ops {
61        s.push_str(&alloc::format!(
62            "else if (strcmp(op, \"{}\") == 0) return this->{}(req);\n",
63            op.name,
64            op.method_name,
65        ));
66    }
67    s.push_str("else throw CORBA::BAD_OPERATION();\n");
68    s
69}
70
71fn render_csharp(class_name: &str, ops: &[SkeletonOp]) -> String {
72    let mut s = alloc::format!("// C# skeleton dispatch for {class_name}\nswitch (op) {{\n");
73    for op in ops {
74        s.push_str(&alloc::format!(
75            "    case \"{}\": return this.{}(req);\n",
76            op.name,
77            op.method_name,
78        ));
79    }
80    s.push_str("    default: throw new omg.org.CORBA.BAD_OPERATION();\n}\n");
81    s
82}
83
84fn render_java(class_name: &str, ops: &[SkeletonOp]) -> String {
85    let mut s = alloc::format!("// Java skeleton dispatch for {class_name}\nswitch (op) {{\n");
86    for op in ops {
87        s.push_str(&alloc::format!(
88            "    case \"{}\": return this.{}(req);\n",
89            op.name,
90            op.method_name,
91        ));
92    }
93    s.push_str("    default: throw new org.omg.CORBA.BAD_OPERATION();\n}\n");
94    s
95}
96
97#[cfg(test)]
98#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
99mod tests {
100    use super::*;
101
102    fn ops() -> alloc::vec::Vec<SkeletonOp> {
103        alloc::vec![
104            SkeletonOp {
105                name: "echo".into(),
106                method_name: "echo".into(),
107            },
108            SkeletonOp {
109                name: "ping".into(),
110                method_name: "Ping".into(),
111            },
112        ]
113    }
114
115    #[test]
116    fn cpp_dispatch_uses_strcmp_and_bad_operation() {
117        let s = render_skeleton_dispatch("EchoImpl", &ops(), TargetLanguage::Cpp);
118        assert!(s.contains("strcmp(op, \"echo\")"));
119        assert!(s.contains("CORBA::BAD_OPERATION"));
120    }
121
122    #[test]
123    fn csharp_dispatch_uses_switch_and_omg_org_namespace() {
124        let s = render_skeleton_dispatch("EchoImpl", &ops(), TargetLanguage::CSharp);
125        assert!(s.contains("switch (op)"));
126        assert!(s.contains("omg.org.CORBA.BAD_OPERATION"));
127    }
128
129    #[test]
130    fn java_dispatch_uses_org_omg_namespace() {
131        let s = render_skeleton_dispatch("EchoImpl", &ops(), TargetLanguage::Java);
132        assert!(s.contains("org.omg.CORBA.BAD_OPERATION"));
133    }
134}