use super::{
MetaCallError, MetaCallGetAttributeError, MetaCallNull, MetaCallSetAttributeError,
MetaCallValue,
};
use crate::{
bindings::{
metacall_object_get, metacall_object_set, metacall_value_destroy, metacall_value_to_object,
metacallv_object,
},
cast, cstring_enum,
};
use std::{
ffi::c_void,
fmt::{self, Debug, Formatter},
};
#[allow(unused_imports)]
use super::MetaCallClass;
pub struct MetaCallObject {
value: *mut c_void,
leak: bool,
}
unsafe impl Send for MetaCallObject {}
unsafe impl Sync for MetaCallObject {}
impl Clone for MetaCallObject {
fn clone(&self) -> Self {
Self {
leak: true,
value: self.value,
}
}
}
impl Debug for MetaCallObject {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "MetaCallObject {{ ... }}")
}
}
impl MetaCallObject {
#[doc(hidden)]
pub fn new_raw(value: *mut c_void) -> Self {
Self { value, leak: false }
}
#[doc(hidden)]
pub fn new_raw_leak(value: *mut c_void) -> Self {
Self { value, leak: true }
}
fn get_attribute_inner(
&self,
name: impl ToString,
) -> Result<*mut c_void, MetaCallGetAttributeError> {
let c_name = cstring_enum!(name, MetaCallGetAttributeError)?;
Ok(unsafe { metacall_object_get(metacall_value_to_object(self.value), c_name.as_ptr()) })
}
pub fn get_attribute_untyped(
&self,
name: impl ToString,
) -> Result<Box<dyn MetaCallValue>, MetaCallGetAttributeError> {
Ok(cast::raw_to_metacallobj_untyped(
self.get_attribute_inner(name)?,
))
}
pub fn get_attribute<T: MetaCallValue>(
&self,
name: impl ToString,
) -> Result<T, MetaCallGetAttributeError> {
match cast::raw_to_metacallobj::<T>(self.get_attribute_inner(name)?) {
Ok(ret) => Ok(ret),
Err(original) => Err(MetaCallGetAttributeError::FailedCasting(original)),
}
}
pub fn set_attribute(
&self,
key: impl ToString,
value: impl MetaCallValue,
) -> Result<(), MetaCallSetAttributeError> {
let c_key = cstring_enum!(key, MetaCallSetAttributeError)?;
let c_arg = cast::metacallobj_to_raw(value);
if unsafe {
metacall_object_set(metacall_value_to_object(self.value), c_key.as_ptr(), c_arg)
} != 0
{
return Err(MetaCallSetAttributeError::SetAttributeFailure);
}
unsafe { metacall_value_destroy(c_arg) };
Ok(())
}
fn call_method_inner<T: MetaCallValue>(
&self,
key: impl ToString,
args: impl IntoIterator<Item = T>,
) -> Result<*mut c_void, MetaCallError> {
let c_key = cstring_enum!(key, MetaCallError)?;
let mut c_args = cast::metacallobj_to_raw_args(args);
let ret = unsafe {
metacallv_object(
metacall_value_to_object(self.value),
c_key.as_ptr(),
c_args.as_mut_ptr(),
c_args.len(),
)
};
for c_arg in c_args {
unsafe { metacall_value_destroy(c_arg) };
}
Ok(ret)
}
pub fn call_method_untyped<T: MetaCallValue>(
&self,
key: impl ToString,
args: impl IntoIterator<Item = T>,
) -> Result<Box<dyn MetaCallValue>, MetaCallError> {
Ok(cast::raw_to_metacallobj_untyped(
self.call_method_inner::<T>(key, args)?,
))
}
pub fn call_method_untyped_no_arg<T: MetaCallValue>(
&self,
key: impl ToString,
) -> Result<Box<dyn MetaCallValue>, MetaCallError> {
Ok(cast::raw_to_metacallobj_untyped(
self.call_method_inner::<T>(key, [])?,
))
}
pub fn call_method<T: MetaCallValue, U: MetaCallValue>(
&self,
key: impl ToString,
args: impl IntoIterator<Item = U>,
) -> Result<T, MetaCallError> {
match cast::raw_to_metacallobj::<T>(self.call_method_inner::<U>(key, args)?) {
Ok(ret) => Ok(ret),
Err(original) => Err(MetaCallError::FailedCasting(original)),
}
}
pub fn call_method_no_arg<T: MetaCallValue>(
&self,
key: impl ToString,
) -> Result<T, MetaCallError> {
self.call_method::<T, MetaCallNull>(key, [])
}
#[doc(hidden)]
pub fn into_raw(mut self) -> *mut c_void {
self.leak = true;
self.value
}
}
impl Drop for MetaCallObject {
fn drop(&mut self) {
if !self.leak {
unsafe { metacall_value_destroy(self.value) }
}
}
}