use std::ops::Deref;
use super::zval::{IntoZvalDyn, Zval};
use crate::{
bindings::_call_user_function_impl,
errors::{Error, Result},
};
#[derive(Debug)]
pub struct Callable<'a>(OwnedZval<'a>);
#[derive(Debug)]
enum OwnedZval<'a> {
Reference(&'a Zval),
Owned(Zval),
}
impl<'a> OwnedZval<'a> {
fn as_ref(&self) -> &Zval {
match self {
OwnedZval::Reference(zv) => *zv,
OwnedZval::Owned(zv) => zv,
}
}
}
impl<'a> Deref for OwnedZval<'a> {
type Target = Zval;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> Callable<'a> {
pub fn new(callable: &'a Zval) -> Result<Self> {
if callable.is_callable() {
Ok(Self(OwnedZval::Reference(callable)))
} else {
Err(Error::Callable)
}
}
pub fn new_owned(callable: Zval) -> Result<Self> {
if callable.is_callable() {
Ok(Self(OwnedZval::Owned(callable)))
} else {
Err(Error::Callable)
}
}
pub fn try_from_name(name: &str) -> Result<Self> {
let mut callable = Zval::new();
callable.set_string(name, false)?;
Self::new_owned(callable)
}
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
if !self.0.is_callable() {
return Err(Error::Callable);
}
let mut retval = Zval::new();
let len = params.len();
let params = params
.into_iter()
.map(|val| val.as_zval(false))
.collect::<Result<Vec<_>>>()?;
let packed = params.into_boxed_slice();
let result = unsafe {
_call_user_function_impl(
std::ptr::null_mut(),
std::mem::transmute(self.0.as_ref()),
&mut retval,
len as _,
packed.as_ptr() as *mut _,
std::ptr::null_mut(),
)
};
if result < 0 {
Err(Error::Callable)
} else {
Ok(retval)
}
}
}