use crate::core::config::{Language, ResolvedCrateConfig};
use crate::core::ir::ApiSurface;
use crate::docs::template_env;
pub(super) fn render_trait_bridges(api: &ApiSurface, config: &ResolvedCrateConfig, lang: Language) -> String {
let Some(backend) = crate::cli::registry::try_get_backend(lang) else {
return String::new();
};
let surfaces = backend.trait_bridge_registration_surface(api, config);
if surfaces.is_empty() {
return String::new();
}
let mut out = String::new();
out.push_str(&template_env::render(
"heading.jinja",
minijinja::context! { marker => "###", title => "Trait Bridges" },
));
out.push_str("| Trait | Register | Unregister | Clear |\n");
out.push_str("|-------|----------|------------|-------|\n");
for surface in &surfaces {
out.push_str(&template_env::render(
"trait_bridge_row.jinja",
minijinja::context! {
trait_name => &surface.trait_name,
register => surface.register_symbol.as_deref().unwrap_or("—"),
unregister => surface.unregister_symbol.as_deref().unwrap_or("—"),
clear => surface.clear_symbol.as_deref().unwrap_or("—"),
},
));
}
out.push('\n');
out
}
#[cfg(test)]
mod tests {
use super::render_trait_bridges;
use crate::core::backend::TraitBridgeRegistrationSurface;
use crate::core::config::{Language, TraitBridgeConfig};
use crate::core::ir::TypeDef;
use crate::docs::test_helpers::{empty_api, make_test_config};
#[test]
fn pyo3_reference_page_reports_the_backends_configured_registration_names() {
let mut config = make_test_config();
config.trait_bridges.push(TraitBridgeConfig {
trait_name: "SampleTrait".to_owned(),
registry_getter: Some("sample::registry::get_sample_registry".to_owned()),
register_fn: Some("register_sample".to_owned()),
unregister_fn: Some("unregister_sample".to_owned()),
clear_fn: Some("clear_samples".to_owned()),
..Default::default()
});
let mut api = empty_api();
api.types.push(TypeDef {
name: "SampleTrait".to_owned(),
rust_path: "sample::SampleTrait".to_owned(),
is_trait: true,
..Default::default()
});
let rendered = render_trait_bridges(&api, &config, Language::Python);
assert!(rendered.contains("### Trait Bridges"), "got:\n{rendered}");
assert!(rendered.contains("`SampleTrait`"), "got:\n{rendered}");
assert!(rendered.contains("register_sample"), "got:\n{rendered}");
assert!(rendered.contains("unregister_sample"), "got:\n{rendered}");
assert!(rendered.contains("clear_samples"), "got:\n{rendered}");
}
#[test]
fn renders_nothing_when_backend_reports_no_registration_surface() {
let config = make_test_config();
let api = empty_api();
let rendered = render_trait_bridges(&api, &config, Language::Python);
assert_eq!(
rendered, "",
"no configured trait bridges must render no section at all"
);
}
#[test]
fn renders_nothing_for_a_backend_not_yet_covered_by_the_default_impl() {
let mut config = make_test_config();
config.trait_bridges.push(TraitBridgeConfig {
trait_name: "SampleTrait".to_owned(),
register_fn: Some("register_sample".to_owned()),
..Default::default()
});
let api = empty_api();
let rendered = render_trait_bridges(&api, &config, Language::KotlinAndroid);
assert_eq!(
rendered, "",
"an uncovered backend must render nothing rather than a placeholder"
);
}
#[test]
fn registration_surface_struct_carries_all_reported_symbols() {
let surface = TraitBridgeRegistrationSurface {
trait_name: "SampleTrait".to_owned(),
register_symbol: Some("register_sample".to_owned()),
unregister_symbol: Some("unregister_sample".to_owned()),
clear_symbol: Some("clear_samples".to_owned()),
};
assert_eq!(surface.trait_name, "SampleTrait");
assert_eq!(surface.register_symbol.as_deref(), Some("register_sample"));
assert_eq!(surface.unregister_symbol.as_deref(), Some("unregister_sample"));
assert_eq!(surface.clear_symbol.as_deref(), Some("clear_samples"));
}
}