use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::Ident;
use weaveffi_core::abi::{AbiParam, CType, ConstPos};
use weaveffi_core::model::FnBinding;
use weaveffi_ir::ir::TypeRef;
use super::{unsupported, PREFIX};
pub(crate) fn ident(name: &str) -> Ident {
Ident::new(name, Span::call_site())
}
pub(crate) fn ctype_to_rust(ct: &CType) -> TokenStream {
match ct {
CType::Int8 => quote!(i8),
CType::Int16 => quote!(i16),
CType::Int32 => quote!(i32),
CType::Int64 => quote!(i64),
CType::Uint8 => quote!(u8),
CType::Uint16 => quote!(u16),
CType::Uint32 => quote!(u32),
CType::Uint64 => quote!(u64),
CType::Float => quote!(f32),
CType::Double => quote!(f64),
CType::Bool => quote!(bool),
CType::Size => quote!(usize),
CType::Char => quote!(::std::os::raw::c_char),
CType::Void => quote!(::std::ffi::c_void),
CType::Handle => quote!(u64),
CType::CancelToken => quote!(::weaveffi::abi::weaveffi_cancel_token),
CType::Error => quote!(::weaveffi::abi::weaveffi_error),
CType::Enum { .. } => quote!(i32),
CType::StructTag { name, .. } => {
let ty = ident(name);
quote!(#ty)
}
CType::Named(core) => {
let ty = ident(&format!("{PREFIX}_{core}"));
quote!(#ty)
}
CType::Ptr { konst, pointee } => {
let inner = ctype_to_rust(pointee);
match konst {
ConstPos::None => quote!(*mut #inner),
ConstPos::West | ConstPos::East => quote!(*const #inner),
}
}
}
}
pub(crate) fn slot_tokens(p: &AbiParam) -> TokenStream {
let n = ident(&p.name);
let t = ctype_to_rust(&p.ty);
quote!(#n: #t)
}
pub(crate) fn ret_arrow(ret: &CType) -> TokenStream {
if matches!(ret, CType::Void) {
TokenStream::new()
} else {
let t = ctype_to_rust(ret);
quote!(-> #t)
}
}
pub(crate) fn sentinel(ret: &CType) -> TokenStream {
match ret {
CType::Void => quote!(()),
CType::Ptr {
konst: ConstPos::None,
..
} => quote!(::std::ptr::null_mut()),
CType::Ptr { .. } => quote!(::std::ptr::null()),
CType::Bool => quote!(false),
CType::Float | CType::Double => quote!(0.0),
_ => quote!(0),
}
}
pub(crate) fn is_copy(ty: &TypeRef) -> bool {
matches!(
ty,
TypeRef::I8
| TypeRef::I16
| TypeRef::I32
| TypeRef::I64
| TypeRef::U8
| TypeRef::U16
| TypeRef::U32
| TypeRef::U64
| TypeRef::F32
| TypeRef::F64
| TypeRef::Bool
| TypeRef::Handle
| TypeRef::Enum(_)
)
}
pub(crate) fn rust_type_ident(name: &str) -> Ident {
ident(name.rsplit('.').next().unwrap_or(name))
}
pub(crate) fn user_param_type<'a>(sig: &'a syn::Signature, name: &str) -> Option<&'a syn::Type> {
sig.inputs.iter().find_map(|arg| {
let syn::FnArg::Typed(pt) = arg else {
return None;
};
let syn::Pat::Ident(id) = pt.pat.as_ref() else {
return None;
};
(id.ident == name).then(|| pt.ty.as_ref())
})
}
pub(crate) fn slot_tokens_for(p: &AbiParam, f: &FnBinding, sig: &syn::Signature) -> TokenStream {
if p.name == "self" {
let t = ctype_to_rust(&p.ty);
return quote!(__wv_self: #t);
}
if let Some(ty) = typed_handle_user_type(p, f, sig) {
let n = ident(&p.name);
quote!(#n: #ty)
} else {
slot_tokens(p)
}
}
pub(crate) fn fn_slots(
params: &[AbiParam],
f: &FnBinding,
sig: &syn::Signature,
) -> Vec<TokenStream> {
params.iter().map(|p| slot_tokens_for(p, f, sig)).collect()
}
fn typed_handle_user_type<'a>(
p: &AbiParam,
f: &FnBinding,
sig: &'a syn::Signature,
) -> Option<&'a syn::Type> {
let pb = f.params.iter().find(|pb| pb.name == p.name)?;
if !matches!(pb.ty, TypeRef::TypedHandle(_)) {
return None;
}
user_param_type(sig, &p.name)
}
pub(crate) fn ret_arrow_for(ret: &CType, f: &FnBinding, sig: &syn::Signature) -> TokenStream {
if matches!(f.ret, Some(TypeRef::TypedHandle(_))) {
if let syn::ReturnType::Type(_, ty) = &sig.output {
let ty = weaveffi_bridge::peel_result(ty);
return quote!(-> #ty);
}
}
ret_arrow(ret)
}
pub(crate) fn param_is_ref(sig: &syn::Signature, name: &str) -> bool {
sig.inputs.iter().any(|arg| {
let syn::FnArg::Typed(pt) = arg else {
return false;
};
let syn::Pat::Ident(id) = pt.pat.as_ref() else {
return false;
};
id.ident == name
&& matches!(pt.ty.as_ref(), syn::Type::Reference(r) if r.mutability.is_none())
})
}
pub(crate) enum CallTarget {
Free,
Static(Ident),
Method(Ident),
}
impl CallTarget {
pub(crate) fn call(&self, fn_name: &str, args: &[TokenStream]) -> TokenStream {
let f = ident(fn_name);
match self {
CallTarget::Free => quote!(#f(#(#args),*)),
CallTarget::Static(ty) => quote!(#ty::#f(#(#args),*)),
CallTarget::Method(_) => quote!(__wv_obj.#f(#(#args),*)),
}
}
pub(crate) fn self_preamble(&self, sentinel: &TokenStream) -> TokenStream {
match self {
CallTarget::Method(ty) => quote! {
if __wv_self.is_null() {
::weaveffi::abi::error_set(out_err, -1, "self is null");
return #sentinel;
}
let __wv_obj: &#ty = unsafe { &*__wv_self };
},
_ => TokenStream::new(),
}
}
}
pub(crate) fn wrap_unwind(body: TokenStream, sentinel: Option<&TokenStream>) -> TokenStream {
let tail = match sentinel {
Some(s) => quote!(#s),
None => TokenStream::new(),
};
quote! {
match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(move || {
#body
})) {
::std::result::Result::Ok(__wv_v) => __wv_v,
::std::result::Result::Err(__wv_panic) => {
::weaveffi::abi::error_set_panic(out_err, &*__wv_panic);
#tail
}
}
}
}
pub(crate) fn typeref_to_rust(ty: &TypeRef) -> syn::Result<TokenStream> {
Ok(match ty {
TypeRef::I8 => quote!(i8),
TypeRef::I16 => quote!(i16),
TypeRef::I32 => quote!(i32),
TypeRef::I64 => quote!(i64),
TypeRef::U8 => quote!(u8),
TypeRef::U16 => quote!(u16),
TypeRef::U32 => quote!(u32),
TypeRef::U64 => quote!(u64),
TypeRef::F32 => quote!(f32),
TypeRef::F64 => quote!(f64),
TypeRef::Bool => quote!(bool),
TypeRef::Handle => quote!(u64),
TypeRef::StringUtf8 | TypeRef::BorrowedStr => quote!(::std::string::String),
TypeRef::Bytes | TypeRef::BorrowedBytes => quote!(::std::vec::Vec<u8>),
TypeRef::Record(s) | TypeRef::RichEnum(s) | TypeRef::Enum(s) | TypeRef::Interface(s) => {
let ty = rust_type_ident(s);
quote!(#ty)
}
TypeRef::Named(n) => unreachable!("unresolved type reference '{n}'"),
TypeRef::TypedHandle(n) => {
let ty = rust_type_ident(n);
quote!(*mut #ty)
}
TypeRef::Optional(inner) => {
let inner = typeref_to_rust(inner)?;
quote!(::std::option::Option<#inner>)
}
TypeRef::List(inner) => {
let inner = typeref_to_rust(inner)?;
quote!(::std::vec::Vec<#inner>)
}
TypeRef::Map(k, v) => {
let k = typeref_to_rust(k)?;
let v = typeref_to_rust(v)?;
quote!(::std::collections::HashMap<#k, #v>)
}
TypeRef::Iterator(_) => return Err(unsupported("type", "nested iterator")),
})
}