use std::{ffi::CString, mem, os::raw::c_char, ptr};
use crate::errors::Result;
use crate::{bindings::zend_function_entry, errors::Error};
use super::{
args::{Arg, ArgInfo},
enums::DataType,
execution_data::ExecutionData,
types::zval::Zval,
types::ZendType,
};
pub type FunctionEntry = zend_function_entry;
impl FunctionEntry {
pub fn end() -> Self {
Self {
fname: ptr::null() as *const c_char,
handler: None,
arg_info: ptr::null(),
num_args: 0,
flags: 0,
}
}
pub fn into_raw(self) -> *mut Self {
Box::into_raw(Box::new(self))
}
}
pub type FunctionHandler = extern "C" fn(execute_data: &mut ExecutionData, retval: &mut Zval);
type FunctionPointerHandler = extern "C" fn(execute_data: *mut ExecutionData, retval: *mut Zval);
#[derive(Debug, Clone)]
pub struct FunctionBuilder<'a> {
name: String,
function: FunctionEntry,
args: Vec<Arg<'a>>,
n_req: Option<usize>,
retval: Option<DataType>,
ret_as_ref: bool,
ret_as_null: bool,
}
impl<'a> FunctionBuilder<'a> {
pub fn new<T: Into<String>>(name: T, handler: FunctionHandler) -> Self {
Self {
name: name.into(),
function: FunctionEntry {
fname: ptr::null(),
handler: Some(unsafe {
mem::transmute::<FunctionHandler, FunctionPointerHandler>(handler)
}),
arg_info: ptr::null(),
num_args: 0,
flags: 0, },
args: vec![],
n_req: None,
retval: None,
ret_as_ref: false,
ret_as_null: false,
}
}
pub fn constructor(handler: FunctionHandler) -> Self {
Self::new("__construct", handler)
}
pub fn arg(mut self, arg: Arg<'a>) -> Self {
self.args.push(arg);
self
}
pub fn not_required(mut self) -> Self {
self.n_req = Some(self.args.len());
self
}
pub fn returns(mut self, type_: DataType, as_ref: bool, allow_null: bool) -> Self {
self.retval = Some(type_);
self.ret_as_ref = as_ref;
self.ret_as_null = allow_null;
self
}
pub fn build(mut self) -> Result<FunctionEntry> {
let mut args = Vec::with_capacity(self.args.len() + 1);
args.push(ArgInfo {
name: self.n_req.unwrap_or(self.args.len()) as *const i8,
type_: match self.retval {
Some(retval) => {
ZendType::empty_from_type(retval, self.ret_as_ref, false, self.ret_as_null)
.ok_or(Error::InvalidCString)?
}
None => ZendType::empty(false, false),
},
default_value: ptr::null(),
});
args.extend(
self.args
.iter()
.map(|arg| arg.as_arg_info())
.collect::<Result<Vec<_>>>()?,
);
self.function.fname = CString::new(self.name)?.into_raw();
self.function.num_args = (args.len() - 1) as u32;
self.function.arg_info = Box::into_raw(args.into_boxed_slice()) as *const ArgInfo;
Ok(self.function)
}
}