use crate::core::config::{BridgeBinding, TraitBridgeConfig};
use crate::core::ir::{ApiSurface, FieldDef, FunctionDef, ParamDef, TypeRef};
#[test]
fn visitor_bridge_uses_configured_context_and_result_metadata() {
let (api, trait_type, bridge) = crate::codegen::visitor_context::test_support::neutral_visitor_fixture();
let output = crate::backends::rustler::trait_bridge::gen_trait_bridge(
&trait_type,
&bridge,
"sample_core",
"SampleError",
"SampleError::Message { message: {msg} }",
&api,
)
.expect("visitor bridge should generate");
crate::codegen::visitor_context::test_support::assert_neutral_visitor_output(&output.code);
assert!(output.code.contains("\"display_name\""));
}
#[test]
fn options_field_bridge_renders_visitor_setup_template() {
let api = ApiSurface {
crate_name: "sample".to_string(),
version: "1.0.0".to_string(),
types: vec![],
functions: vec![],
enums: vec![],
errors: vec![],
excluded_type_paths: std::collections::HashMap::new(),
excluded_trait_names: std::collections::HashSet::new(),
services: vec![],
handler_contracts: vec![],
unsupported_public_items: vec![],
};
let func = FunctionDef {
name: "render".to_string(),
rust_path: "sample::render".to_string(),
original_rust_path: String::new(),
params: vec![
ParamDef {
name: "html".to_string(),
ty: TypeRef::String,
is_ref: true,
..ParamDef::default()
},
ParamDef {
name: "options".to_string(),
ty: TypeRef::Named("RenderOptions".to_string()),
..ParamDef::default()
},
],
return_type: TypeRef::String,
is_async: false,
error_type: Some("RenderError".to_string()),
doc: String::new(),
cfg: None,
sanitized: false,
return_sanitized: false,
returns_ref: false,
returns_cow: false,
return_newtype_wrapper: None,
binding_excluded: false,
binding_exclusion_reason: None,
version: Default::default(),
};
let field = FieldDef {
name: "renderer".to_string(),
ty: TypeRef::Named("RenderVisitorHandle".to_string()),
..FieldDef::default()
};
let bridge = TraitBridgeConfig {
trait_name: "RenderVisitor".to_string(),
super_trait: None,
registry_getter: None,
register_fn: None,
unregister_fn: None,
clear_fn: None,
type_alias: Some("RenderVisitorHandle".to_string()),
param_name: None,
register_extra_args: None,
exclude_languages: vec![],
ffi_skip_methods: vec![],
bind_via: BridgeBinding::OptionsField,
options_type: Some("RenderOptions".to_string()),
options_field: Some("renderer".to_string()),
context_type: None,
result_type: None,
};
let bridge_match = crate::codegen::generators::trait_bridge::BridgeFieldMatch {
param_index: 1,
param_name: "options".to_string(),
options_type: "RenderOptions".to_string(),
param_is_optional: false,
field_name: "renderer".to_string(),
field: &field,
bridge: &bridge,
};
let code = crate::backends::rustler::trait_bridge::gen_bridge_field_function(
&api,
&func,
&bridge_match,
&bridge,
&crate::backends::rustler::type_map::RustlerMapper,
&ahash::AHashSet::new(),
&ahash::AHashSet::new(),
"sample",
);
let expected_options_setup = concat!(
"let mut options_core: sample::options::RenderOptions = options.map(|s| ",
"serde_json::from_str::<sample::options::RenderOptions>(&s).unwrap_or_default())",
".unwrap_or_default();"
);
assert!(code.contains(expected_options_setup));
assert!(code.contains("let bridge = ElixirRenderVisitorBridge::new(env, pid, visitor_term);"));
assert!(code.contains(
"options_core.renderer = Some(std::sync::Arc::new(std::sync::Mutex::new(bridge)) as sample::RenderVisitorHandle);"
));
}