extern crate alloc;
use zerodds_idl::ast::types::{Export, InterfaceDef, OpDecl, ParamAttribute, ParamDecl, TypeSpec};
use crate::error::Result;
pub fn emit_interface(
out: &mut String,
i: &InterfaceDef,
registry: &crate::emitter::InterfaceRegistry<'_>,
) -> Result<()> {
emit_interface_trait(out, i)?;
out.push('\n');
emit_interface_stub(out, i, registry)?;
out.push('\n');
emit_interface_skeleton(out, i)?;
Ok(())
}
fn emit_interface_trait(out: &mut String, i: &InterfaceDef) -> Result<()> {
let name = &i.name.text;
out.push_str("/// Generated by `zerodds-corba-rust` from IDL interface.\n");
emit_interface_exceptions_enum(out, i)?;
let repo_id = zerodds_corba_codegen::build_repository_id(&[], name, 1, 0);
let mut bases_iter = i.bases.iter().map(|s| {
s.parts
.iter()
.map(|p| p.text.as_str())
.collect::<Vec<_>>()
.join("::")
});
let bases_clause = if let Some(first) = bases_iter.next() {
let mut s = format!(": {first} + ::core::marker::Send + ::core::marker::Sync");
for base in bases_iter {
s.push_str(&format!(" + {base}"));
}
s
} else {
": ::core::marker::Send + ::core::marker::Sync".to_string()
};
let repo_const = name.to_ascii_uppercase();
out.push_str(&format!(
"/// CORBA Repository-ID fuer `{name}` (Spec §10.7.3.1).\npub const {repo_const}_REPOSITORY_ID: &str = \"{repo_id}\";\n"
));
out.push_str(&format!("pub trait {name}{bases_clause} {{\n"));
for export in &i.exports {
emit_export_trait_method(out, export)?;
}
out.push_str("}\n");
Ok(())
}
fn emit_interface_exceptions_enum(out: &mut String, i: &InterfaceDef) -> Result<()> {
let name = &i.name.text;
let mut all_raises: alloc::collections::BTreeSet<String> = alloc::collections::BTreeSet::new();
for export in &i.exports {
if let Export::Op(op) = export {
for raise in &op.raises {
let r = raise
.parts
.iter()
.map(|p| p.text.as_str())
.collect::<Vec<_>>()
.join("::");
all_raises.insert(r);
}
}
}
if all_raises.is_empty() {
return Ok(());
}
out.push_str(&format!(
"/// Exception-Enum fuer `{name}` — vereinigt alle in `raises`-Listen referenzierten User-Exceptions plus System-Exception (Spec §3.5).\n"
));
out.push_str("#[derive(Debug, Clone)]\n");
out.push_str(&format!("pub enum {name}Error {{\n"));
out.push_str(
" /// CORBA-System-Exception (BAD_OPERATION, MARSHAL, OBJECT_NOT_EXIST, ...).\n",
);
out.push_str(" System(zerodds_corba_rust::CorbaException),\n");
for raise in &all_raises {
let variant = raise.split("::").last().unwrap_or(raise);
out.push_str(&format!(
" /// User-Exception `{raise}` (raised by `{name}`-Operations).\n"
));
out.push_str(&format!(" {variant}({raise}),\n"));
}
out.push_str("}\n\n");
Ok(())
}
fn emit_export_trait_method(out: &mut String, export: &Export) -> Result<()> {
match export {
Export::Op(op) => emit_op_trait_method(out, op),
Export::Attr(attr) => emit_attr_trait_method(out, attr),
_ => Ok(()),
}
}
pub fn emit_op_trait_method_pub(out: &mut String, op: &OpDecl) -> Result<()> {
emit_op_trait_method(out, op)
}
pub fn emit_attr_trait_method_pub(
out: &mut String,
attr: &zerodds_idl::ast::types::AttrDecl,
) -> Result<()> {
emit_attr_trait_method(out, attr)
}
fn emit_op_trait_method(out: &mut String, op: &OpDecl) -> Result<()> {
let method = &op.name.text;
let receiver = if op_is_state_changing(op) {
"&mut self"
} else {
"&self"
};
let params_str = render_params(&op.params)?;
let return_str = render_return(&op.return_type, &op.raises)?;
let comma = if params_str.is_empty() { "" } else { ", " };
out.push_str(&format!(
" /// IDL operation `{method}`.\n fn {method}({receiver}{comma}{params_str}) -> {return_str};\n"
));
Ok(())
}
fn emit_attr_trait_method(
out: &mut String,
attr: &zerodds_idl::ast::types::AttrDecl,
) -> Result<()> {
let name = &attr.name.text;
let ty = zerodds_idl_rust::type_map::rust_type_for(&attr.type_spec)?;
out.push_str(&format!(" /// IDL attribute `{name}` getter.\n"));
out.push_str(&format!(
" fn {name}(&self) -> ::core::result::Result<{ty}, zerodds_corba_rust::CorbaException>;\n"
));
if !attr.readonly {
out.push_str(&format!(" /// IDL attribute `{name}` setter.\n"));
out.push_str(&format!(
" fn set_{name}(&mut self, value: {ty}) -> ::core::result::Result<(), zerodds_corba_rust::CorbaException>;\n"
));
}
Ok(())
}
fn op_is_state_changing(op: &OpDecl) -> bool {
op.params
.iter()
.any(|p| matches!(p.attribute, ParamAttribute::Out | ParamAttribute::InOut))
}
fn render_params(params: &[ParamDecl]) -> Result<String> {
let mut out = String::new();
for (idx, p) in params.iter().enumerate() {
if idx > 0 {
out.push_str(", ");
}
let ty = zerodds_idl_rust::type_map::rust_type_for(&p.type_spec)?;
let name = &p.name.text;
match p.attribute {
ParamAttribute::In => {
out.push_str(&format!("{name}: {ty}"));
}
ParamAttribute::Out | ParamAttribute::InOut => {
out.push_str(&format!("{name}: &mut {ty}"));
}
}
}
Ok(out)
}
fn render_return(
return_type: &Option<TypeSpec>,
_raises: &[zerodds_idl::ast::types::ScopedName],
) -> Result<String> {
let inner = match return_type {
None => "()".to_string(),
Some(ts) => zerodds_idl_rust::type_map::rust_type_for(ts)?,
};
Ok(format!(
"::core::result::Result<{inner}, zerodds_corba_rust::CorbaException>"
))
}
fn emit_interface_stub(
out: &mut String,
i: &InterfaceDef,
registry: &crate::emitter::InterfaceRegistry<'_>,
) -> Result<()> {
let name = &i.name.text;
let stub_name = format!("{name}Stub");
out.push_str(&format!(
"/// Client-Stub fuer `{name}` (sendet GIOP-Requests an einen Remote-Object).\n"
));
out.push_str(&format!("pub struct {stub_name} {{\n"));
out.push_str(" /// Object-Reference (IOR) des Remote-Servants.\n");
out.push_str(" pub object_ref: zerodds_corba_rust::ObjectReference,\n");
out.push_str("}\n\n");
out.push_str(&format!("impl {stub_name} {{\n"));
out.push_str(" /// Konstruiert einen Stub mit einer ObjectReference.\n");
out.push_str(" #[must_use]\n");
out.push_str(" pub fn new(object_ref: zerodds_corba_rust::ObjectReference) -> Self {\n");
out.push_str(" Self { object_ref }\n");
out.push_str(" }\n");
out.push_str("}\n\n");
out.push_str(&format!("impl {name} for {stub_name} {{\n"));
for export in &i.exports {
emit_export_stub_impl(out, name, export)?;
}
out.push_str("}\n");
let mut visited = alloc::collections::BTreeSet::new();
for base in &i.bases {
emit_base_impls_recursive(out, &stub_name, base, registry, &mut visited)?;
}
Ok(())
}
fn emit_base_impls_recursive(
out: &mut String,
stub_name: &str,
base_path: &zerodds_idl::ast::types::ScopedName,
registry: &crate::emitter::InterfaceRegistry<'_>,
visited: &mut alloc::collections::BTreeSet<String>,
) -> Result<()> {
let base_simple = base_path
.parts
.last()
.map(|p| p.text.clone())
.unwrap_or_default();
if base_simple.is_empty() || !visited.insert(base_simple.clone()) {
return Ok(());
}
let Some(base_def) = registry.get(&base_simple) else {
return Ok(());
};
let base_full_path = base_path
.parts
.iter()
.map(|p| p.text.as_str())
.collect::<Vec<_>>()
.join("::");
out.push_str(&format!(
"\n/// Base-Inheritance: `{base_simple}`-Methoden auf `{stub_name}` (Spec §3.6).\n"
));
out.push_str(&format!("impl {base_full_path} for {stub_name} {{\n"));
for export in &base_def.exports {
emit_export_stub_impl(out, &base_simple, export)?;
}
out.push_str("}\n");
for grandbase in &base_def.bases {
emit_base_impls_recursive(out, stub_name, grandbase, registry, visited)?;
}
Ok(())
}
fn emit_export_stub_impl(out: &mut String, _iface: &str, export: &Export) -> Result<()> {
match export {
Export::Op(op) => emit_op_stub_impl(out, op),
Export::Attr(attr) => emit_attr_stub_impl(out, attr),
_ => Ok(()),
}
}
fn emit_op_stub_impl(out: &mut String, op: &OpDecl) -> Result<()> {
let method = &op.name.text;
let receiver = if op_is_state_changing(op) {
"&mut self"
} else {
"&self"
};
let params_str = render_params(&op.params)?;
let return_str = render_return(&op.return_type, &op.raises)?;
let comma = if params_str.is_empty() { "" } else { ", " };
out.push_str(&format!(
" fn {method}({receiver}{comma}{params_str}) -> {return_str} {{\n"
));
out.push_str(&format!(
" // GIOP-Request: operation_name = \"{method}\".\n"
));
out.push_str(
" // Phase-2: encode in_params + send via corba-iiop, await reply, decode.\n",
);
out.push_str(
" ::core::result::Result::Err(zerodds_corba_rust::CorbaException::SystemException {\n",
);
out.push_str(" minor: 0,\n");
out.push_str(" message: \"corba-rust stub: GIOP marshalling not yet wired\",\n");
out.push_str(" })\n");
out.push_str(" }\n");
Ok(())
}
fn emit_attr_stub_impl(out: &mut String, attr: &zerodds_idl::ast::types::AttrDecl) -> Result<()> {
let name = &attr.name.text;
let ty = zerodds_idl_rust::type_map::rust_type_for(&attr.type_spec)?;
out.push_str(&format!(
" fn {name}(&self) -> ::core::result::Result<{ty}, zerodds_corba_rust::CorbaException> {{\n"
));
out.push_str(
" ::core::result::Result::Err(zerodds_corba_rust::CorbaException::SystemException {\n",
);
out.push_str(" minor: 0,\n");
out.push_str(&format!(
" message: \"corba-rust stub: attribute `{name}` getter not yet wired\",\n"
));
out.push_str(" })\n");
out.push_str(" }\n");
if !attr.readonly {
out.push_str(&format!(
" fn set_{name}(&mut self, value: {ty}) -> ::core::result::Result<(), zerodds_corba_rust::CorbaException> {{\n"
));
out.push_str(" let _ = value;\n");
out.push_str(
" ::core::result::Result::Err(zerodds_corba_rust::CorbaException::SystemException {\n",
);
out.push_str(" minor: 0,\n");
out.push_str(&format!(
" message: \"corba-rust stub: attribute `{name}` setter not yet wired\",\n"
));
out.push_str(" })\n");
out.push_str(" }\n");
}
Ok(())
}
fn emit_interface_skeleton(out: &mut String, i: &InterfaceDef) -> Result<()> {
let name = &i.name.text;
out.push_str(&format!("/// Server-Skeleton-Dispatch fuer `{name}`.\n"));
out.push_str("/// Wird vom POA aufgerufen wenn ein GIOP-Request mit Target-Operation\n");
out.push_str(&format!(
"/// `{name}` empfangen wird. Dispatched die Operation an den Servant.\n"
));
let name_lower = name.to_lowercase();
out.push_str(&format!(
"pub fn dispatch_{name_lower}(servant: &dyn {name}, operation: &str, _payload: &[u8]) -> zerodds_corba_rust::SkeletonResult {{\n"
));
out.push_str(" match operation {\n");
for export in &i.exports {
emit_export_skeleton_arm(out, export);
}
out.push_str(" _ => zerodds_corba_rust::SkeletonResult::BadOperation,\n");
out.push_str(" }\n");
out.push_str("}\n");
Ok(())
}
fn emit_export_skeleton_arm(out: &mut String, export: &Export) {
match export {
Export::Op(op) => {
let method = &op.name.text;
out.push_str(&format!(
" \"{method}\" => {{\n // Phase-2: GIOP-Body decoden, servant.{method}(...) aufrufen, Reply encoden.\n let _ = servant;\n zerodds_corba_rust::SkeletonResult::NotYetWired\n }}\n"
));
}
Export::Attr(attr) => {
let name = &attr.name.text;
out.push_str(&format!(
" \"_get_{name}\" => {{\n let _ = servant;\n zerodds_corba_rust::SkeletonResult::NotYetWired\n }}\n"
));
if !attr.readonly {
out.push_str(&format!(
" \"_set_{name}\" => {{\n let _ = servant;\n zerodds_corba_rust::SkeletonResult::NotYetWired\n }}\n"
));
}
}
_ => {}
}
}