use super::*;
pub struct Function {
pub(crate) name: Ident,
args: FunctionArgs,
block: Box<Block>,
output: FunctionRet,
is_extern: bool,
}
impl Function {
pub fn from(item_fn: ItemFn) -> GenResult<Self> {
let args = FunctionArgs::from(item_fn.sig.inputs)?;
Ok(Self {
name: item_fn.sig.ident,
args,
block: item_fn.block,
output: FunctionRet::from(item_fn.sig.output)?,
is_extern: false,
})
}
pub fn set_extern(&mut self, is_extern: bool) {
self.is_extern = is_extern;
}
pub fn generate_rust_function(&self) -> impl quote::ToTokens {
let ident = &self.name;
let body = self.generate_body();
let docstring = format!("`{}({}) -> {}`", ident, self.args, self.output);
let code = if self.is_extern {
quote! {
#[doc = #docstring]
#[no_mangle]
pub extern fn #ident(vm: &mut Vm) -> lovm2_core::prelude::Lovm2Result<()> {
#body
}
}
} else {
quote! {
#[doc = #docstring]
pub fn #ident(vm: &mut Vm) -> lovm2_core::prelude::Lovm2Result<()> {
#body
}
}
};
code
}
fn generate_body(&self) -> impl quote::ToTokens {
let prelude = self.generate_prelude();
let postlude = self.generate_postlude();
let block = &self.block;
let output = &self.output.as_tokens();
let args = self.args.as_tokens();
let argscall = self.args.generate_call();
quote! {
#[inline]
fn _lv2_wrapper(#args) -> #output #block
#prelude
let _lv2_return_value: #output = #argscall;
#postlude
}
}
fn generate_postlude(&self) -> impl quote::ToTokens {
self.output.generate()
}
fn generate_prelude(&self) -> impl quote::ToTokens {
self.args.generate()
}
}
pub(crate) fn accept_type(ty: &syn::Type) -> GenResult<&syn::Type> {
match ty {
syn::Type::Path(ty_path) => {
if let Some(_ident) = ty_path.path.get_ident() {
return Ok(ty);
}
}
syn::Type::Reference(ref_type) => {
accept_type(&ref_type.elem)?;
return Ok(ty);
}
_ => {}
}
Err(format!("unexpected type {:?}", ty))
}