use std::any::TypeId;
use std::cell::RefCell;
use std::collections::HashSet;
use std::ffi::CString;
use std::marker::PhantomData;
use std::ptr;
use crate::core_types::{GodotString, VariantType};
use crate::export::*;
use crate::object::NewRef;
use crate::private::get_api;
#[derive(Debug)]
pub struct ClassBuilder<C> {
pub(super) init_handle: *mut libc::c_void,
pub(super) class_name: CString,
mixins: RefCell<HashSet<TypeId, ahash::RandomState>>,
_marker: PhantomData<C>,
}
impl<C: NativeClass> ClassBuilder<C> {
pub(crate) fn new(init_handle: *mut libc::c_void, class_name: CString) -> Self {
Self {
init_handle,
class_name,
mixins: RefCell::default(),
_marker: PhantomData,
}
}
#[inline]
pub fn method<'a, F: Method<C>>(&'a self, name: &'a str, method: F) -> MethodBuilder<'a, C, F> {
MethodBuilder::new(self, name, method)
}
#[inline]
pub fn property<'a, T>(&'a self, name: &'a str) -> PropertyBuilder<'a, C, T>
where
T: Export,
{
PropertyBuilder::new(self, name)
}
#[inline]
pub fn signal(&self, name: &str) -> SignalBuilder<C> {
SignalBuilder::new(self, GodotString::from(name))
}
#[inline]
pub(crate) fn add_signal(&self, signal: Signal) {
unsafe {
let args_and_hints = signal
.args
.iter()
.map(|arg| {
let hint_string = arg.export_info.hint_string.new_ref();
(arg, hint_string)
})
.collect::<Vec<_>>();
let mut sys_args = args_and_hints
.iter()
.map(|(param, hint_string)| sys::godot_signal_argument {
name: param.name.to_sys(),
type_: Self::get_param_type(param) as i32,
hint: param.export_info.hint_kind,
hint_string: hint_string.to_sys(),
usage: param.usage.to_sys(),
default_value: param.default.to_sys(),
})
.collect::<Vec<_>>();
(get_api().godot_nativescript_register_signal)(
self.init_handle,
self.class_name.as_ptr(),
&sys::godot_signal {
name: signal.name.to_sys(),
num_args: sys_args.len() as i32,
args: sys_args.as_mut_ptr(),
num_default_args: 0,
default_args: ptr::null_mut(),
},
);
}
}
fn get_param_type(arg: &SignalParam) -> VariantType {
let export_type = arg.export_info.variant_type;
if export_type != VariantType::Nil {
export_type
} else {
arg.default.get_type()
}
}
pub(crate) fn add_method(&self, method: ScriptMethod) {
let method_name = CString::new(method.name).unwrap();
let attr = sys::godot_method_attributes {
rpc_type: method.attributes.rpc_mode.sys(),
};
let method_desc = sys::godot_instance_method {
method: method.method_ptr,
method_data: method.method_data,
free_func: method.free_func,
};
unsafe {
(get_api().godot_nativescript_register_method)(
self.init_handle,
self.class_name.as_ptr() as *const _,
method_name.as_ptr() as *const _,
attr,
method_desc,
);
}
}
#[inline]
pub fn mixin<M: Mixin<C>>(&self) {
if self.mixins.borrow_mut().insert(TypeId::of::<M>()) {
M::register(self);
}
}
}
pub trait Mixin<C>: crate::private::mixin::Sealed + 'static
where
C: NativeClass,
{
#[doc(hidden)]
fn register(builder: &ClassBuilder<C>);
}