use crate::codegen::generators::trait_bridge::{bridge_param_type as param_type, visitor_param_type};
use crate::core::config::TraitBridgeConfig;
use crate::core::ir::{ApiSurface, MethodDef, TypeDef};
use std::collections::HashMap;
pub(super) fn gen_visitor_bridge(
trait_type: &TypeDef,
bridge_cfg: &TraitBridgeConfig,
struct_name: &str,
trait_path: &str,
core_crate: &str,
type_paths: &HashMap<String, String>,
api: &ApiSurface,
) -> anyhow::Result<String> {
let result_metadata = crate::codegen::visitor_result::required_visitor_result_metadata(api, bridge_cfg)?;
let context_helper = crate::codegen::visitor_context::visitor_context_helper(
api,
bridge_cfg,
core_crate,
crate::codegen::visitor_context::VisitorContextBackend::Pyo3,
)?;
let helper_fn = crate::backends::pyo3::template_env::render(
"trait_bridge/nodecontext_to_py_dict.jinja",
minijinja::context! {
context_type_path => context_helper.type_path,
context_field_lines => context_helper.field_lines,
},
);
let struct_def = crate::backends::pyo3::template_env::render(
"trait_bridge/visitor_struct.jinja",
minijinja::context! {
struct_name => struct_name,
},
);
let mut methods_code = String::new();
for method in crate::codegen::generators::trait_bridge::visitor_callback_methods(trait_type, bridge_cfg) {
gen_visitor_method(
&mut methods_code,
method,
trait_path,
bridge_cfg,
type_paths,
&result_metadata,
);
}
let mut out = String::with_capacity(4096);
out.push_str(&helper_fn);
out.push_str(&struct_def);
out.push_str(&crate::backends::pyo3::template_env::render(
"trait_bridge/impl_header.jinja",
minijinja::context! { trait_path => trait_path, struct_name => struct_name },
));
out.push_str(&methods_code);
out.push_str("}\n");
Ok(out)
}
fn gen_visitor_method(
out: &mut String,
method: &MethodDef,
_trait_path: &str,
bridge_cfg: &TraitBridgeConfig,
type_paths: &HashMap<String, String>,
result_metadata: &crate::codegen::visitor_result::VisitorResultMetadata,
) {
use crate::core::ir::TypeRef;
let name = &method.name;
let mut sig_parts = vec!["&mut self".to_string()];
for p in &method.params {
let ty_str = visitor_param_type(&p.ty, p.is_ref, p.optional, type_paths);
sig_parts.push(format!("{}: {}", p.name, ty_str));
}
let sig = sig_parts.join(", ");
let ret_ty = match &method.return_type {
TypeRef::Named(n) => type_paths.get(n).cloned().unwrap_or_else(|| n.clone()),
other => param_type(other, "", false, type_paths),
};
let py_args = build_visitor_py_args(method, bridge_cfg);
let py_call = if py_args.is_empty() {
format!("obj.call_method0(\"{name}\")")
} else {
format!("obj.call_method1(\"{name}\", ({py_args}))")
};
let method_code = crate::backends::pyo3::template_env::render(
"trait_bridge/visitor_method.jinja",
minijinja::context! {
method_name => name,
sig => sig,
ret_ty => ret_ty,
default_result_expr => crate::codegen::visitor_result::default_result_expr(&ret_ty, result_metadata),
unknown_string_result_expr => crate::codegen::visitor_result::unknown_string_result_expr(
&ret_ty,
result_metadata,
"s",
),
unit_result_variants => crate::codegen::visitor_result::variant_contexts(&result_metadata.unit_variants),
payload_result_variants => crate::codegen::visitor_result::variant_contexts(
&result_metadata.string_payload_variants,
),
py_call => py_call,
},
);
out.push_str(&method_code);
}
fn build_visitor_py_args(method: &MethodDef, bridge_cfg: &TraitBridgeConfig) -> String {
use crate::core::ir::TypeRef;
let args: Vec<String> = method
.params
.iter()
.map(|p| {
if let TypeRef::Named(n) = &p.ty {
if Some(n.as_str()) == bridge_cfg.context_type.as_deref() {
return if p.is_ref {
format!("nodecontext_to_py_dict(py, {})", p.name)
} else {
format!("nodecontext_to_py_dict(py, &{})", p.name)
};
}
}
if p.optional && matches!(&p.ty, TypeRef::String) && p.is_ref {
return p.name.clone();
}
if p.is_ref {
if let TypeRef::Vec(inner) = &p.ty {
if matches!(inner.as_ref(), TypeRef::String) {
return p.name.clone();
}
}
}
if let TypeRef::Vec(inner) = &p.ty {
if matches!(inner.as_ref(), TypeRef::String) {
return format!("{}.to_vec()", p.name);
}
}
if let TypeRef::Optional(inner) = &p.ty {
if matches!(inner.as_ref(), TypeRef::String) {
return p.name.clone();
}
}
if matches!(&p.ty, TypeRef::String) && p.is_ref {
return p.name.clone();
}
if matches!(&p.ty, TypeRef::String) {
return format!("{}.as_str()", p.name);
}
p.name.clone()
})
.collect();
if args.len() == 1 {
format!("{},", args[0])
} else {
args.join(", ")
}
}