use std::collections::{BTreeMap, HashMap};
use zerodds_idl::ast::types::{Definition, Export, InterfaceDcl, InterfaceDef, Specification};
use crate::error::Result;
fn collect_exception_repo_ids(spec: &Specification) -> BTreeMap<String, String> {
let mut prefixes: BTreeMap<String, String> = BTreeMap::new();
collect_prefixes(&spec.definitions, &mut prefixes);
let mut out = BTreeMap::new();
let mut path: Vec<String> = Vec::new();
walk_exceptions(&spec.definitions, &mut path, &prefixes, &mut out);
out
}
fn collect_prefixes(defs: &[Definition], prefixes: &mut BTreeMap<String, String>) {
for d in defs {
match d {
Definition::TypePrefix(tp) => {
if let Some(last) = tp.target.parts.last() {
prefixes.insert(last.text.clone(), tp.prefix.clone());
}
}
Definition::Module(m) => collect_prefixes(&m.definitions, prefixes),
_ => {}
}
}
}
fn rid_for(path: &[String], name: &str) -> String {
let parts: Vec<&str> = path.iter().map(String::as_str).collect();
zerodds_corba_codegen::build_repository_id(&parts, name, 1, 0)
}
fn walk_exceptions(
defs: &[Definition],
path: &mut Vec<String>,
prefixes: &BTreeMap<String, String>,
out: &mut BTreeMap<String, String>,
) {
for d in defs {
match d {
Definition::Except(e) => {
out.insert(e.name.text.clone(), rid_for(path, &e.name.text));
}
Definition::Module(m) => {
let mut pushed = 0;
if let Some(p) = prefixes.get(&m.name.text) {
for seg in p.split('/').filter(|s| !s.is_empty()) {
path.push(seg.to_string());
pushed += 1;
}
}
path.push(m.name.text.clone());
pushed += 1;
walk_exceptions(&m.definitions, path, prefixes, out);
for _ in 0..pushed {
path.pop();
}
}
Definition::Interface(InterfaceDcl::Def(i)) => {
path.push(i.name.text.clone());
for ex in &i.exports {
if let Export::Except(e) = ex {
out.insert(e.name.text.clone(), rid_for(path, &e.name.text));
}
}
path.pop();
}
_ => {}
}
}
}
pub(crate) type InterfaceRegistry<'a> = HashMap<String, &'a InterfaceDef>;
#[derive(Debug, Clone, Default)]
pub struct CorbaRustGenOptions {
pub header_comment: Option<String>,
}
pub fn generate_corba_rust_module(
spec: &Specification,
opts: &CorbaRustGenOptions,
) -> Result<String> {
zerodds_idl_rust::type_map::set_any_target_corba(true);
let mut out = String::new();
out.push_str("// SPDX-License-Identifier: Apache-2.0\n");
if let Some(c) = &opts.header_comment {
for line in c.lines() {
out.push_str("// ");
out.push_str(line);
out.push('\n');
}
}
out.push_str("// Auto-generated by `zerodds-corba-rust`. Do not edit by hand.\n\n");
out.push_str("#![allow(clippy::too_many_lines)]\n");
out.push_str("#![allow(unused_imports, unused_variables)]\n\n");
let mut registry: InterfaceRegistry<'_> = HashMap::new();
collect_interfaces(spec, &mut registry);
zerodds_idl_rust::type_map::set_interface_refs(registry.keys().cloned());
crate::interface_emit::set_exception_repo_ids(collect_exception_repo_ids(spec));
crate::valuetype_emit::register_value_states(spec)?;
for def in &spec.definitions {
emit_definition(&mut out, def, ®istry, &[])?;
}
Ok(out)
}
fn collect_interfaces<'a>(spec: &'a Specification, reg: &mut InterfaceRegistry<'a>) {
for def in &spec.definitions {
collect_from_def(def, reg);
}
}
fn collect_from_def<'a>(def: &'a Definition, reg: &mut InterfaceRegistry<'a>) {
use zerodds_idl::ast::types::InterfaceDcl;
match def {
Definition::Interface(InterfaceDcl::Def(d)) => {
reg.insert(d.name.text.clone(), d);
}
Definition::Module(m) => {
for inner in &m.definitions {
collect_from_def(inner, reg);
}
}
_ => {}
}
}
fn emit_definition(
out: &mut String,
def: &Definition,
registry: &InterfaceRegistry<'_>,
scope: &[&str],
) -> Result<()> {
match def {
Definition::Module(m) => emit_module(out, m, registry, scope)?,
Definition::Interface(i_dcl) => {
use zerodds_idl::ast::types::InterfaceDcl;
match i_dcl {
InterfaceDcl::Def(def) => {
crate::interface_emit::emit_interface(out, def, registry)?;
out.push('\n');
}
InterfaceDcl::Forward(_) => {}
}
}
Definition::ValueDef(v) => {
crate::valuetype_emit::emit_valuetype(out, v, scope)?;
out.push('\n');
}
Definition::Component(c) => {
crate::component_emit::emit_component(out, c)?;
out.push('\n');
}
Definition::Home(h) => {
crate::component_emit::emit_home(out, h)?;
out.push('\n');
}
_ => {}
}
Ok(())
}
fn emit_module(
out: &mut String,
m: &zerodds_idl::ast::types::ModuleDef,
registry: &InterfaceRegistry<'_>,
scope: &[&str],
) -> Result<()> {
out.push_str("pub mod ");
out.push_str(&m.name.text);
out.push_str(" {\n");
let mut child_scope: Vec<&str> = scope.to_vec();
child_scope.push(&m.name.text);
for inner in &m.definitions {
let mut inner_out = String::new();
emit_definition(&mut inner_out, inner, registry, &child_scope)?;
for line in inner_out.lines() {
if line.is_empty() {
out.push('\n');
} else {
out.push_str(" ");
out.push_str(line);
out.push('\n');
}
}
}
out.push_str("}\n\n");
Ok(())
}