use quote::quote;
use syn::{
parse::Parser, parse_quote, punctuated::Punctuated, token::Unsafe, Attribute, FnArg,
ForeignItem, Ident, ImplItem, ReturnType,
};
use super::{
unqualify::{unqualify_params, unqualify_ret_type},
RsCodegenResult,
};
use crate::types::make_ident;
use crate::{
conversion::{
analysis::fun::{ArgumentAnalysis, FnAnalysisBody},
api::ImplBlockDetails,
},
types::{Namespace, TypeName},
};
pub(crate) fn gen_function(ns: &Namespace, analysis: FnAnalysisBody) -> RsCodegenResult {
let rename_using_rust_attr = analysis.rename_using_rust_attr;
let cxxbridge_name = analysis.cxxbridge_name;
let rust_name = analysis.rust_name;
let self_ty = analysis.self_ty;
let ret_type = analysis.ret_type;
let is_constructor = analysis.is_constructor;
let param_details = analysis.param_details;
let cpp_call_name = analysis.cpp_call_name;
let wrapper_function_needed = analysis.wrapper_function_needed;
let requires_unsafe = analysis.requires_unsafe;
let params = analysis.params;
let vis = analysis.vis;
let mut cpp_name_attr = Vec::new();
let mut impl_entry = None;
let unsafety: Option<Unsafe> = if requires_unsafe {
Some(parse_quote!(unsafe))
} else {
None
};
if cxxbridge_name != rust_name {
if let Some(type_name) = &self_ty {
impl_entry = Some(generate_method_impl(
¶m_details,
is_constructor,
type_name,
&cxxbridge_name,
&rust_name,
&ret_type,
&unsafety,
));
}
}
let rust_name_attr: Vec<_> = if rename_using_rust_attr {
Attribute::parse_outer
.parse2(quote!(
#[rust_name = #rust_name]
))
.unwrap()
} else {
Vec::new()
};
if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
cpp_name_attr = Attribute::parse_outer
.parse2(quote!(
#[cxx_name = #cpp_call_name]
))
.unwrap();
}
let params = unqualify_params(params);
let ret_type = unqualify_ret_type(ret_type);
let namespace_attr = if ns.is_empty() || wrapper_function_needed {
Vec::new()
} else {
let namespace_string = ns.to_string();
Attribute::parse_outer
.parse2(quote!(
#[namespace = #namespace_string]
))
.unwrap()
};
let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
#(#namespace_attr)*
#(#rust_name_attr)*
#(#cpp_name_attr)*
#vis #unsafety fn #cxxbridge_name ( #params ) #ret_type;
));
RsCodegenResult {
extern_c_mod_item: Some(extern_c_mod_item),
bridge_items: Vec::new(),
global_items: Vec::new(),
bindgen_mod_item: None,
impl_entry,
}
}
fn generate_method_impl(
param_details: &[ArgumentAnalysis],
is_constructor: bool,
impl_block_type_name: &TypeName,
cxxbridge_name: &Ident,
rust_name: &str,
ret_type: &ReturnType,
unsafety: &Option<Unsafe>,
) -> Box<ImplBlockDetails> {
let mut wrapper_params: Punctuated<FnArg, syn::Token![,]> = Punctuated::new();
let mut arg_list = Vec::new();
for pd in param_details {
let type_name = pd.conversion.converted_rust_type();
let wrapper_arg_name = if pd.self_type.is_some() && !is_constructor {
parse_quote!(self)
} else {
pd.name.clone()
};
wrapper_params.push(parse_quote!(
#wrapper_arg_name: #type_name
));
arg_list.push(wrapper_arg_name);
}
let rust_name = make_ident(&rust_name);
Box::new(ImplBlockDetails {
item: ImplItem::Method(parse_quote! {
pub #unsafety fn #rust_name ( #wrapper_params ) #ret_type {
cxxbridge::#cxxbridge_name ( #(#arg_list),* )
}
}),
ty: make_ident(impl_block_type_name.get_final_ident()),
})
}