use alef_core::config::TraitBridgeConfig;
use alef_core::ir::{MethodDef, TypeDef};
use heck::ToSnakeCase;
use std::fmt::Write;
pub struct TraitBridgeSpec<'a> {
pub trait_def: &'a TypeDef,
pub bridge_config: &'a TraitBridgeConfig,
pub core_import: &'a str,
pub wrapper_prefix: &'a str,
}
impl<'a> TraitBridgeSpec<'a> {
pub fn wrapper_name(&self) -> String {
format!("{}{}Bridge", self.wrapper_prefix, self.trait_def.name)
}
pub fn trait_snake(&self) -> String {
self.trait_def.name.to_snake_case()
}
pub fn trait_path(&self) -> String {
self.trait_def.rust_path.replace('-', "_")
}
pub fn required_methods(&self) -> Vec<&'a MethodDef> {
self.trait_def.methods.iter().filter(|m| !m.has_default_impl).collect()
}
pub fn optional_methods(&self) -> Vec<&'a MethodDef> {
self.trait_def.methods.iter().filter(|m| m.has_default_impl).collect()
}
}
pub trait TraitBridgeGenerator {
fn foreign_object_type(&self) -> &str;
fn bridge_imports(&self) -> Vec<String>;
fn gen_sync_method_body(&self, method: &MethodDef, spec: &TraitBridgeSpec) -> String;
fn gen_async_method_body(&self, method: &MethodDef, spec: &TraitBridgeSpec) -> String;
fn gen_constructor(&self, spec: &TraitBridgeSpec) -> String;
fn gen_registration_fn(&self, spec: &TraitBridgeSpec) -> String;
fn registration_fn_attr(&self) -> &str;
}
pub fn gen_bridge_wrapper_struct(spec: &TraitBridgeSpec, generator: &dyn TraitBridgeGenerator) -> String {
let wrapper = spec.wrapper_name();
let foreign_type = generator.foreign_object_type();
let mut out = String::with_capacity(512);
writeln!(
out,
"/// Wrapper that bridges a foreign {prefix} object to the `{trait_name}` trait.",
prefix = spec.wrapper_prefix,
trait_name = spec.trait_def.name,
)
.ok();
writeln!(out, "pub struct {wrapper} {{").ok();
writeln!(out, " inner: {foreign_type},").ok();
writeln!(out, " cached_name: String,").ok();
write!(out, "}}").ok();
out
}
pub fn gen_bridge_plugin_impl(spec: &TraitBridgeSpec, generator: &dyn TraitBridgeGenerator) -> Option<String> {
if !spec.trait_def.super_traits.iter().any(|s| s == "Plugin") {
return None;
}
let wrapper = spec.wrapper_name();
let core_import = spec.core_import;
let mut out = String::with_capacity(1024);
writeln!(out, "impl {core_import}::Plugin for {wrapper} {{").ok();
writeln!(out, " fn name(&self) -> &str {{").ok();
writeln!(out, " &self.cached_name").ok();
writeln!(out, " }}").ok();
writeln!(out).ok();
writeln!(out, " fn version(&self) -> &str {{").ok();
let version_method = MethodDef {
name: "version".to_string(),
params: vec![],
return_type: alef_core::ir::TypeRef::String,
is_async: false,
is_static: false,
error_type: None,
doc: String::new(),
receiver: Some(alef_core::ir::ReceiverKind::Ref),
sanitized: false,
trait_source: None,
returns_ref: true,
returns_cow: false,
return_newtype_wrapper: None,
has_default_impl: false,
};
let version_body = generator.gen_sync_method_body(&version_method, spec);
for line in version_body.lines() {
writeln!(out, " {line}").ok();
}
writeln!(out, " }}").ok();
writeln!(out).ok();
writeln!(
out,
" fn initialize(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {{"
)
.ok();
let init_method = MethodDef {
name: "initialize".to_string(),
params: vec![],
return_type: alef_core::ir::TypeRef::Unit,
is_async: false,
is_static: false,
error_type: Some("Box<dyn std::error::Error + Send + Sync>".to_string()),
doc: String::new(),
receiver: Some(alef_core::ir::ReceiverKind::Ref),
sanitized: false,
trait_source: None,
returns_ref: false,
returns_cow: false,
return_newtype_wrapper: None,
has_default_impl: true,
};
let init_body = generator.gen_sync_method_body(&init_method, spec);
for line in init_body.lines() {
writeln!(out, " {line}").ok();
}
writeln!(out, " }}").ok();
writeln!(out).ok();
writeln!(
out,
" fn shutdown(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {{"
)
.ok();
let shutdown_method = MethodDef {
name: "shutdown".to_string(),
params: vec![],
return_type: alef_core::ir::TypeRef::Unit,
is_async: false,
is_static: false,
error_type: Some("Box<dyn std::error::Error + Send + Sync>".to_string()),
doc: String::new(),
receiver: Some(alef_core::ir::ReceiverKind::Ref),
sanitized: false,
trait_source: None,
returns_ref: false,
returns_cow: false,
return_newtype_wrapper: None,
has_default_impl: true,
};
let shutdown_body = generator.gen_sync_method_body(&shutdown_method, spec);
for line in shutdown_body.lines() {
writeln!(out, " {line}").ok();
}
writeln!(out, " }}").ok();
write!(out, "}}").ok();
Some(out)
}
pub fn gen_bridge_trait_impl(spec: &TraitBridgeSpec, generator: &dyn TraitBridgeGenerator) -> String {
let wrapper = spec.wrapper_name();
let trait_path = spec.trait_path();
let mut out = String::with_capacity(2048);
writeln!(out, "impl {trait_path} for {wrapper} {{").ok();
for (i, method) in spec.trait_def.methods.iter().enumerate() {
if i > 0 {
writeln!(out).ok();
}
let async_kw = if method.is_async { "async " } else { "" };
let receiver = match &method.receiver {
Some(alef_core::ir::ReceiverKind::Ref) => "&self",
Some(alef_core::ir::ReceiverKind::RefMut) => "&mut self",
Some(alef_core::ir::ReceiverKind::Owned) => "self",
None => "",
};
let params: Vec<String> = method
.params
.iter()
.map(|p| format!("{}: {}", p.name, format_type_ref(&p.ty)))
.collect();
let all_params = if receiver.is_empty() {
params.join(", ")
} else if params.is_empty() {
receiver.to_string()
} else {
format!("{}, {}", receiver, params.join(", "))
};
let ret = format_return_type(&method.return_type, method.error_type.as_deref());
writeln!(out, " {async_kw}fn {}({all_params}) -> {ret} {{", method.name).ok();
let body = if method.is_async {
generator.gen_async_method_body(method, spec)
} else {
generator.gen_sync_method_body(method, spec)
};
for line in body.lines() {
writeln!(out, " {line}").ok();
}
writeln!(out, " }}").ok();
}
write!(out, "}}").ok();
out
}
pub fn gen_bridge_registration_fn(spec: &TraitBridgeSpec, generator: &dyn TraitBridgeGenerator) -> Option<String> {
let reg_fn = spec.bridge_config.register_fn.as_deref()?;
let attr = generator.registration_fn_attr();
let mut out = String::with_capacity(1024);
writeln!(out, "{attr}").ok();
writeln!(out, "pub fn {reg_fn}() -> Result<(), String> {{").ok();
let body = generator.gen_registration_fn(spec);
for line in body.lines() {
writeln!(out, " {line}").ok();
}
writeln!(out, "}}").ok();
Some(out)
}
pub fn gen_bridge_all(spec: &TraitBridgeSpec, generator: &dyn TraitBridgeGenerator) -> String {
let mut out = String::with_capacity(4096);
let imports = generator.bridge_imports();
for imp in &imports {
writeln!(out, "use {imp};").ok();
}
if !imports.is_empty() {
writeln!(out).ok();
}
out.push_str(&gen_bridge_wrapper_struct(spec, generator));
writeln!(out).ok();
writeln!(out).ok();
if let Some(plugin_impl) = gen_bridge_plugin_impl(spec, generator) {
out.push_str(&plugin_impl);
writeln!(out).ok();
writeln!(out).ok();
}
out.push_str(&gen_bridge_trait_impl(spec, generator));
if let Some(reg_fn_code) = gen_bridge_registration_fn(spec, generator) {
writeln!(out).ok();
writeln!(out).ok();
out.push_str(®_fn_code);
}
out
}
fn format_type_ref(ty: &alef_core::ir::TypeRef) -> String {
use alef_core::ir::{PrimitiveType, TypeRef};
match ty {
TypeRef::Primitive(p) => match p {
PrimitiveType::Bool => "bool",
PrimitiveType::U8 => "u8",
PrimitiveType::U16 => "u16",
PrimitiveType::U32 => "u32",
PrimitiveType::U64 => "u64",
PrimitiveType::I8 => "i8",
PrimitiveType::I16 => "i16",
PrimitiveType::I32 => "i32",
PrimitiveType::I64 => "i64",
PrimitiveType::F32 => "f32",
PrimitiveType::F64 => "f64",
PrimitiveType::Usize => "usize",
PrimitiveType::Isize => "isize",
}
.to_string(),
TypeRef::String => "String".to_string(),
TypeRef::Char => "char".to_string(),
TypeRef::Bytes => "Vec<u8>".to_string(),
TypeRef::Optional(inner) => format!("Option<{}>", format_type_ref(inner)),
TypeRef::Vec(inner) => format!("Vec<{}>", format_type_ref(inner)),
TypeRef::Map(k, v) => format!(
"std::collections::HashMap<{}, {}>",
format_type_ref(k),
format_type_ref(v)
),
TypeRef::Named(name) => name.clone(),
TypeRef::Path => "std::path::PathBuf".to_string(),
TypeRef::Unit => "()".to_string(),
TypeRef::Json => "serde_json::Value".to_string(),
TypeRef::Duration => "std::time::Duration".to_string(),
}
}
fn format_return_type(ty: &alef_core::ir::TypeRef, error_type: Option<&str>) -> String {
let inner = format_type_ref(ty);
match error_type {
Some(err) => format!("Result<{inner}, {err}>"),
None => inner,
}
}