use zerodds_idl::ast::types::{Export, InterfaceDef, OpDecl, ParamAttribute};
use crate::error::Result;
const MARSHAL_EXC: &str = "zerodds_corba_rust::CorbaException::SystemException { minor: 0u32, message: \"CORBA MARSHAL: CDR error\" }";
fn op_has_ami(op: &OpDecl) -> bool {
op.annotations
.iter()
.any(|a| a.name.parts.last().is_some_and(|p| p.text == "ami"))
}
fn iface_has_ami(i: &InterfaceDef) -> bool {
let on_iface = i
.annotations
.iter()
.any(|a| a.name.parts.last().is_some_and(|p| p.text == "ami"));
on_iface
|| i.exports.iter().any(|e| match e {
Export::Op(op) => op_has_ami(op),
_ => false,
})
}
fn ami_ops(i: &InterfaceDef) -> Vec<&OpDecl> {
let iface_ami = i
.annotations
.iter()
.any(|a| a.name.parts.last().is_some_and(|p| p.text == "ami"));
i.exports
.iter()
.filter_map(|e| match e {
Export::Op(op) if !op.oneway && (iface_ami || op_has_ami(op)) => Some(op),
_ => None,
})
.collect()
}
fn in_params(op: &OpDecl) -> Result<String> {
let mut out = String::new();
for p in &op.params {
if matches!(p.attribute, ParamAttribute::In | ParamAttribute::InOut) {
let ty = zerodds_idl_rust::type_map::rust_type_for(&p.type_spec)?;
out.push_str(&format!(", {}: {ty}", p.name.text));
}
}
Ok(out)
}
fn out_params(op: &OpDecl) -> Result<Vec<(String, String)>> {
let mut out = Vec::new();
for p in &op.params {
if matches!(p.attribute, ParamAttribute::Out | ParamAttribute::InOut) {
let ty = zerodds_idl_rust::type_map::rust_type_for(&p.type_spec)?;
out.push((p.name.text.clone(), ty));
}
}
Ok(out)
}
fn emit_in_marshal(out: &mut String, op: &OpDecl, indent: &str) {
for p in &op.params {
if matches!(p.attribute, ParamAttribute::In | ParamAttribute::InOut) {
out.push_str(&format!(
"{indent}<_ as zerodds_cdr::CdrEncode>::encode(&{}, &mut __w).map_err(|_| {MARSHAL_EXC})?;\n",
p.name.text
));
}
}
}
pub fn emit_ami(out: &mut String, i: &InterfaceDef) -> Result<()> {
if !iface_has_ami(i) {
return Ok(());
}
let ops = ami_ops(i);
if ops.is_empty() {
return Ok(());
}
let iface = &i.name.text;
let stub = format!("{iface}Stub");
out.push_str(&format!(
"/// AMI ReplyHandler for `{iface}` (CORBA Messaging §22.5): per operation a\n/// success callback `<op>` and a fault callback `<op>_excep`.\n"
));
out.push_str(&format!(
"pub trait {iface}AmiHandler: ::core::marker::Send + ::core::marker::Sync {{\n"
));
for op in &ops {
let m = &op.name.text;
let mut sig = String::new();
if let Some(ts) = &op.return_type {
let ty = zerodds_idl_rust::type_map::rust_type_for(ts)?;
sig.push_str(&format!(", __return: {ty}"));
}
for (name, ty) in out_params(op)? {
sig.push_str(&format!(", {name}: {ty}"));
}
out.push_str(&format!(
" /// Reply for `{m}` (return value + out/inout args, decl order).\n"
));
out.push_str(&format!(" fn {m}(&self{sig});\n"));
out.push_str(&format!(
" /// Fault for `{m}` (system or user exception via ExceptionHolder).\n"
));
out.push_str(&format!(
" fn {m}_excep(&self, __excep: zerodds_corba_rust::CorbaException);\n"
));
}
out.push_str("}\n\n");
for op in &ops {
let m = &op.name.text;
let poller = format!("{iface}{}Poller", up_first(m));
out.push_str(&format!(
"/// AMI poller for `{iface}::{m}` (§22.6): holds the `request_id`; `get_reply`\n/// drives the channel until the reply and decodes it typed.\n"
));
out.push_str(&format!(
"pub struct {poller} {{\n /// GIOP `request_id` of the pending invocation.\n pub request_id: u32,\n}}\n\n"
));
out.push_str(&format!("impl {poller} {{\n"));
let ret_ty = poller_return_type(iface, op)?;
out.push_str(&format!(
" /// Fetches the reply (blocking until it arrives) and decodes it.\n ///\n /// # Errors\n /// CORBA exception (system/user) or transport error.\n pub fn get_reply(&self, __channel: &mut dyn zerodds_corba_rust::AsyncCorbaChannel) -> ::core::result::Result<{ret_ty}, zerodds_corba_rust::CorbaException> {{\n"
));
out.push_str(" let (__b, __re) = __channel.get_reply(self.request_id)??;\n");
out.push_str(" let mut __r = zerodds_cdr::BufferReader::new(&__b, __re);\n");
emit_reply_decode(out, op, " ", " ", false)?;
out.push_str(&format!(
" ::core::result::Result::Ok({})\n",
poller_return_expr(op)?
));
out.push_str(" }\n}\n\n");
}
out.push_str(&format!(
"/// AMI invocations on `{stub}` (CORBA Messaging §22).\nimpl {stub} {{\n"
));
for op in &ops {
let m = &op.name.text;
let inp = in_params(op)?;
out.push_str(&format!(
" /// Asynchronous callback call of `{m}` (§22.5). Returns the `request_id`.\n ///\n /// # Errors\n /// Marshalling/transport error on send.\n"
));
out.push_str(&format!(
" pub fn sendc_{m}(&self, __channel: &mut dyn zerodds_corba_rust::AsyncCorbaChannel, __handler: ::std::sync::Arc<dyn {iface}AmiHandler>{inp}) -> ::core::result::Result<u32, zerodds_corba_rust::CorbaException> {{\n"
));
out.push_str(
" let mut __w = zerodds_cdr::BufferWriter::new(zerodds_cdr::Endianness::Big);\n",
);
emit_in_marshal(out, op, " ");
out.push_str(" let __body = __w.into_bytes();\n");
out.push_str(&format!(
" __channel.send(\"{m}\", &__body, ::std::boxed::Box::new(move |__reply| {{\n"
));
out.push_str(" match __reply {\n");
out.push_str(" ::core::result::Result::Ok((__b, __re)) => {\n");
out.push_str(
" let mut __r = zerodds_cdr::BufferReader::new(&__b, __re);\n",
);
emit_reply_decode(
out,
op,
" ",
" ",
true,
)?;
let call_args = handler_call_args(op)?;
out.push_str(&format!(
" __handler.{m}({call_args});\n"
));
out.push_str(" }\n");
out.push_str(&format!(
" ::core::result::Result::Err(__exc) => __handler.{m}_excep(__exc),\n"
));
out.push_str(" }\n");
out.push_str(" }))\n");
out.push_str(" }\n\n");
let poller = format!("{iface}{}Poller", up_first(m));
out.push_str(&format!(
" /// Asynchronous polling call of `{m}` (§22.6). Returns a poller.\n ///\n /// # Errors\n /// Marshalling/transport error on send.\n"
));
out.push_str(&format!(
" pub fn sendp_{m}(&self, __channel: &mut dyn zerodds_corba_rust::AsyncCorbaChannel{inp}) -> ::core::result::Result<{poller}, zerodds_corba_rust::CorbaException> {{\n"
));
out.push_str(
" let mut __w = zerodds_cdr::BufferWriter::new(zerodds_cdr::Endianness::Big);\n",
);
emit_in_marshal(out, op, " ");
out.push_str(&format!(
" let __id = __channel.send_poll(\"{m}\", &__w.into_bytes())?;\n ::core::result::Result::Ok({poller} {{ request_id: __id }})\n }}\n\n"
));
}
out.push_str("}\n\n");
Ok(())
}
fn emit_reply_decode(
out: &mut String,
op: &OpDecl,
indent: &str,
_x: &str,
in_callback: bool,
) -> Result<()> {
let m = &op.name.text;
let on_err = if in_callback {
format!("{{ __handler.{m}_excep({MARSHAL_EXC}); return; }}")
} else {
format!("return ::core::result::Result::Err({MARSHAL_EXC})")
};
if let Some(ts) = &op.return_type {
let ty = zerodds_idl_rust::type_map::rust_type_for(ts)?;
out.push_str(&format!(
"{indent}let __ret = match <{ty} as zerodds_cdr::CdrDecode>::decode(&mut __r) {{ ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(_) => {on_err} }};\n"
));
}
for (name, ty) in out_params(op)? {
out.push_str(&format!(
"{indent}let {name} = match <{ty} as zerodds_cdr::CdrDecode>::decode(&mut __r) {{ ::core::result::Result::Ok(__v) => __v, ::core::result::Result::Err(_) => {on_err} }};\n"
));
}
Ok(())
}
fn handler_call_args(op: &OpDecl) -> Result<String> {
let mut parts = Vec::new();
if op.return_type.is_some() {
parts.push("__ret".to_string());
}
for (name, _) in out_params(op)? {
parts.push(name);
}
Ok(parts.join(", "))
}
fn poller_return_type(_iface: &str, op: &OpDecl) -> Result<String> {
let mut parts = Vec::new();
if let Some(ts) = &op.return_type {
parts.push(zerodds_idl_rust::type_map::rust_type_for(ts)?);
}
for (_, ty) in out_params(op)? {
parts.push(ty);
}
Ok(match parts.len() {
0 => "()".to_string(),
1 => parts.into_iter().next().unwrap_or_default(),
_ => format!("({})", parts.join(", ")),
})
}
fn poller_return_expr(op: &OpDecl) -> Result<String> {
let mut parts = Vec::new();
if op.return_type.is_some() {
parts.push("__ret".to_string());
}
for (name, _) in out_params(op)? {
parts.push(name);
}
Ok(match parts.len() {
0 => "()".to_string(),
1 => parts.into_iter().next().unwrap_or_default(),
_ => format!("({})", parts.join(", ")),
})
}
fn up_first(s: &str) -> String {
let mut c = s.chars();
match c.next() {
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
None => String::new(),
}
}