use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::DeriveInput;
use crate::util::{
create_metadata_items, extract_docstring, ident_to_string, mod_path, tagged_impl_header,
};
use uniffi_meta::ObjectImpl;
pub fn expand_object(input: DeriveInput, udl_mode: bool) -> syn::Result<TokenStream> {
let module_path = mod_path()?;
let ident = &input.ident;
let docstring = extract_docstring(&input.attrs)?;
let name = ident_to_string(ident);
let clone_fn_ident = Ident::new(
&uniffi_meta::clone_fn_symbol_name(&module_path, &name),
Span::call_site(),
);
let free_fn_ident = Ident::new(
&uniffi_meta::free_fn_symbol_name(&module_path, &name),
Span::call_site(),
);
let meta_static_var = (!udl_mode).then(|| {
interface_meta_static_var(ident, ObjectImpl::Struct, &module_path, docstring)
.unwrap_or_else(syn::Error::into_compile_error)
});
let interface_impl = interface_impl(ident, udl_mode);
Ok(quote! {
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #clone_fn_ident(
ptr: *const ::std::ffi::c_void,
call_status: &mut ::uniffi::RustCallStatus
) -> *const ::std::ffi::c_void {
uniffi::rust_call(call_status, || {
unsafe { ::std::sync::Arc::increment_strong_count(ptr) };
Ok(ptr)
})
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #free_fn_ident(
ptr: *const ::std::ffi::c_void,
call_status: &mut ::uniffi::RustCallStatus
) {
uniffi::rust_call(call_status, || {
assert!(!ptr.is_null());
let ptr = ptr.cast::<#ident>();
unsafe {
::std::sync::Arc::decrement_strong_count(ptr);
}
Ok(())
});
}
#interface_impl
#meta_static_var
})
}
pub(crate) fn interface_impl(ident: &Ident, udl_mode: bool) -> TokenStream {
let name = ident_to_string(ident);
let impl_spec = tagged_impl_header("FfiConverterArc", ident, udl_mode);
let lower_return_impl_spec = tagged_impl_header("LowerReturn", ident, udl_mode);
let lift_ref_impl_spec = tagged_impl_header("LiftRef", ident, udl_mode);
let mod_path = match mod_path() {
Ok(p) => p,
Err(e) => return e.into_compile_error(),
};
quote! {
uniffi::deps::static_assertions::assert_impl_all!(#ident: ::core::marker::Sync, ::core::marker::Send);
#[doc(hidden)]
#[automatically_derived]
unsafe #impl_spec {
type FfiType = *const ::std::os::raw::c_void;
fn lower(obj: ::std::sync::Arc<Self>) -> Self::FfiType {
::std::sync::Arc::into_raw(obj) as Self::FfiType
}
fn try_lift(v: Self::FfiType) -> ::uniffi::Result<::std::sync::Arc<Self>> {
let v = v as *const #ident;
Ok(unsafe { ::std::sync::Arc::<Self>::from_raw(v) })
}
fn write(obj: ::std::sync::Arc<Self>, buf: &mut Vec<u8>) {
::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
::uniffi::deps::bytes::BufMut::put_u64(buf, <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::lower(obj) as u64);
}
fn try_read(buf: &mut &[u8]) -> ::uniffi::Result<::std::sync::Arc<Self>> {
::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
::uniffi::check_remaining(buf, 8)?;
<Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::try_lift(::uniffi::deps::bytes::Buf::get_u64(buf) as Self::FfiType)
}
const TYPE_ID_META: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::TYPE_INTERFACE)
.concat_str(#mod_path)
.concat_str(#name);
}
unsafe #lower_return_impl_spec {
type ReturnType = <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::FfiType;
fn lower_return(obj: Self) -> ::std::result::Result<Self::ReturnType, ::uniffi::RustBuffer> {
Ok(<Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::lower(::std::sync::Arc::new(obj)))
}
const TYPE_ID_META: ::uniffi::MetadataBuffer = <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::TYPE_ID_META;
}
unsafe #lift_ref_impl_spec {
type LiftType = ::std::sync::Arc<Self>;
}
}
}
pub(crate) fn interface_meta_static_var(
ident: &Ident,
imp: ObjectImpl,
module_path: &str,
docstring: String,
) -> syn::Result<TokenStream> {
let name = ident_to_string(ident);
let code = match imp {
ObjectImpl::Struct => quote! { ::uniffi::metadata::codes::INTERFACE },
ObjectImpl::Trait => quote! { ::uniffi::metadata::codes::TRAIT_INTERFACE },
ObjectImpl::CallbackTrait => quote! { ::uniffi::metadata::codes::CALLBACK_TRAIT_INTERFACE },
};
Ok(create_metadata_items(
"interface",
&name,
quote! {
::uniffi::MetadataBuffer::from_code(#code)
.concat_str(#module_path)
.concat_str(#name)
.concat_long_str(#docstring)
},
None,
))
}