use super::*;
use crate::core::config::{BridgeBinding, TraitBridgeConfig};
use crate::core::ir::{
ApiSurface, FieldDef, FunctionDef, MethodDef, ParamDef, PrimitiveType, ReceiverKind, TypeDef, TypeRef,
};
use std::collections::HashMap;
fn make_trait_bridge_config(super_trait: Option<&str>, register_fn: Option<&str>) -> TraitBridgeConfig {
TraitBridgeConfig {
trait_name: "OcrBackend".to_string(),
super_trait: super_trait.map(str::to_string),
registry_getter: None,
register_fn: register_fn.map(str::to_string),
unregister_fn: None,
clear_fn: None,
type_alias: None,
param_name: None,
register_extra_args: None,
exclude_languages: Vec::new(),
ffi_skip_methods: Vec::new(),
bind_via: BridgeBinding::FunctionParam,
options_type: None,
options_field: None,
context_type: None,
result_type: None,
}
}
fn make_alias_bridge(trait_name: &str, alias: &str) -> TraitBridgeConfig {
TraitBridgeConfig {
trait_name: trait_name.to_string(),
type_alias: Some(alias.to_string()),
..TraitBridgeConfig::default()
}
}
fn make_type_def(name: &str, rust_path: &str, methods: Vec<MethodDef>) -> TypeDef {
TypeDef {
name: name.to_string(),
rust_path: rust_path.to_string(),
original_rust_path: rust_path.to_string(),
fields: vec![],
methods,
is_opaque: true,
is_clone: false,
is_copy: false,
doc: String::new(),
cfg: None,
is_trait: true,
has_default: false,
has_stripped_cfg_fields: false,
is_return_type: false,
serde_rename_all: None,
has_serde: false,
super_traits: vec![],
binding_excluded: false,
binding_exclusion_reason: None,
is_variant_wrapper: false,
has_lifetime_params: false,
version: Default::default(),
}
}
fn make_method(
name: &str,
params: Vec<ParamDef>,
return_type: TypeRef,
is_async: bool,
has_default_impl: bool,
trait_source: Option<&str>,
error_type: Option<&str>,
) -> MethodDef {
MethodDef {
name: name.to_string(),
params,
return_type,
is_async,
is_static: false,
error_type: error_type.map(str::to_string),
doc: String::new(),
receiver: Some(ReceiverKind::Ref),
sanitized: false,
trait_source: trait_source.map(str::to_string),
returns_ref: false,
returns_cow: false,
return_newtype_wrapper: None,
has_default_impl,
binding_excluded: false,
binding_exclusion_reason: None,
version: Default::default(),
}
}
fn make_func(name: &str, params: Vec<ParamDef>) -> FunctionDef {
FunctionDef {
name: name.to_string(),
rust_path: format!("mylib::{name}"),
original_rust_path: String::new(),
params,
return_type: TypeRef::Unit,
is_async: false,
error_type: None,
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(),
}
}
fn make_field(name: &str, ty: TypeRef) -> FieldDef {
FieldDef {
name: name.to_string(),
ty,
optional: false,
default: None,
doc: String::new(),
sanitized: false,
is_boxed: false,
type_rust_path: None,
cfg: None,
typed_default: None,
core_wrapper: Default::default(),
vec_inner_core_wrapper: Default::default(),
newtype_wrapper: None,
serde_rename: None,
serde_flatten: false,
binding_excluded: false,
binding_exclusion_reason: None,
original_type: None,
}
}
#[test]
fn bridge_wrapper_name_uses_configured_trait_name() {
let bridge = make_alias_bridge("XmlWalker", "WalkerHandle");
assert_eq!(bridge_wrapper_name("Js", &bridge), "JsXmlWalkerBridge");
}
#[test]
fn is_bridge_handle_type_ref_matches_configured_alias_only() {
let bridges = vec![make_alias_bridge("XmlWalker", "WalkerHandle")];
assert!(is_bridge_handle_type_ref(
&TypeRef::Optional(Box::new(TypeRef::Named("WalkerHandle".to_string()))),
&bridges
));
assert!(!is_bridge_handle_type_ref(
&TypeRef::Optional(Box::new(TypeRef::Named("VisitorHandle".to_string()))),
&bridges
));
assert!(!is_bridge_handle_type_ref(
&TypeRef::Named("RenderOptions".to_string()),
&bridges
));
}
fn make_param(name: &str, ty: TypeRef, is_ref: bool) -> ParamDef {
ParamDef {
name: name.to_string(),
ty,
optional: false,
default: None,
sanitized: false,
typed_default: None,
is_ref,
is_mut: false,
newtype_wrapper: None,
original_type: None,
map_is_ahash: false,
map_key_is_cow: false,
vec_inner_is_ref: false,
map_is_btree: false,
core_wrapper: crate::core::ir::CoreWrapper::None,
}
}
fn make_spec<'a>(
trait_def: &'a TypeDef,
bridge_config: &'a TraitBridgeConfig,
wrapper_prefix: &'a str,
type_paths: HashMap<String, String>,
) -> TraitBridgeSpec<'a> {
TraitBridgeSpec {
trait_def,
bridge_config,
core_import: "mylib",
wrapper_prefix,
type_paths,
lifetime_type_names: std::collections::HashSet::new(),
error_type: "MyError".to_string(),
error_constructor: "MyError::from({msg})".to_string(),
}
}
struct MockBridgeGenerator;
impl TraitBridgeGenerator for MockBridgeGenerator {
fn foreign_object_type(&self) -> &str {
"Py<PyAny>"
}
fn bridge_imports(&self) -> Vec<String> {
vec!["pyo3::prelude::*".to_string(), "pyo3::types::PyString".to_string()]
}
fn gen_sync_method_body(&self, method: &MethodDef, _spec: &TraitBridgeSpec) -> String {
format!("// sync body for {}", method.name)
}
fn gen_async_method_body(&self, method: &MethodDef, _spec: &TraitBridgeSpec) -> String {
format!("// async body for {}", method.name)
}
fn gen_constructor(&self, spec: &TraitBridgeSpec) -> String {
format!(
"impl {} {{\n pub fn new(obj: Py<PyAny>) -> Self {{ \
Self {{ inner: obj, cached_name: String::new() }} }}\n}}",
spec.wrapper_name()
)
}
fn gen_registration_fn(&self, spec: &TraitBridgeSpec) -> String {
let fn_name = spec.bridge_config.register_fn.as_deref().unwrap_or("register");
format!("pub fn {fn_name}(obj: Py<PyAny>) {{ /* register */ }}")
}
}
mod generation;
mod lookup;
mod spec_and_formatting;