use std::ffi::CString;
use std::marker::PhantomData;
use sealed::sealed;
use crate::invocable::{GlobalMetadata, MethodMetadata};
use crate::systems::RttiSystemMut;
use crate::types::{CName, NativeClass};
use crate::{class_kind, NativeRepr, RttiSystem, ScriptClass};
#[derive(Debug)]
pub struct ExportList<H, T> {
head: H,
tail: T,
}
impl<H, T> ExportList<H, T> {
pub const fn new(head: H, tail: T) -> Self {
Self { head, tail }
}
}
#[sealed]
pub trait Exportable {
fn register(&self);
fn post_register(&self);
}
#[sealed]
impl<H, T> Exportable for ExportList<H, T>
where
H: Exportable,
T: Exportable,
{
#[inline]
fn register(&self) {
self.head.register();
self.tail.register();
}
#[inline]
fn post_register(&self) {
self.head.post_register();
self.tail.post_register();
}
}
#[derive(Debug)]
pub struct ExportNil;
#[sealed]
impl Exportable for ExportNil {
#[inline]
fn register(&self) {}
#[inline]
fn post_register(&self) {}
}
#[derive(Debug)]
pub struct ClassExport<C: 'static> {
base: &'static str,
methods: &'static [MethodMetadata<C>],
static_methods: &'static [GlobalMetadata],
}
impl<C: ScriptClass> ClassExport<C> {
pub fn builder() -> ClassExportBuilder<C> {
ClassExportBuilder {
base: "IScriptable",
methods: &[],
static_methods: &[],
}
}
}
#[sealed]
impl<C: Default + Clone + ScriptClass<Kind = class_kind::Native>> Exportable for ClassExport<C> {
fn register(&self) {
let mut rtti = RttiSystemMut::get();
let name_cstr = CString::new(C::NAME).expect("name should be valid");
let base = rtti
.get_class(CName::new(self.base))
.expect("base should exist");
let handle = NativeClass::<C>::new_handle(&name_cstr, Some(base));
rtti.register_class(handle);
}
fn post_register(&self) {
let (converted_methods, converted_static_methods) = {
let rtti_ro = RttiSystem::get();
let class = rtti_ro
.get_class(CName::new(C::NAME))
.expect("class should exist");
let converted_methods = self
.methods
.iter()
.map(|m| m.to_rtti(class))
.collect::<Vec<_>>();
let converted_static_methods = self
.static_methods
.iter()
.map(|m| m.to_rtti_static_method(class))
.collect::<Vec<_>>();
(converted_methods, converted_static_methods)
};
let mut rtti_rw = RttiSystemMut::get();
let class = rtti_rw
.get_class(CName::new(C::NAME))
.expect("class should exist");
for method in converted_methods {
class.add_method(method);
}
for static_method in converted_static_methods {
class.add_static_method(static_method);
}
}
}
#[derive(Debug)]
pub struct ClassExportBuilder<C: 'static> {
base: &'static str,
methods: &'static [MethodMetadata<C>],
static_methods: &'static [GlobalMetadata],
}
impl<C> ClassExportBuilder<C> {
pub const fn base(mut self, base: &'static str) -> Self {
self.base = base;
self
}
pub const fn methods(mut self, methods: &'static [MethodMetadata<C>]) -> Self {
self.methods = methods;
self
}
pub const fn static_methods(mut self, static_methods: &'static [GlobalMetadata]) -> Self {
self.static_methods = static_methods;
self
}
pub const fn build(self) -> ClassExport<C> {
ClassExport {
base: self.base,
methods: self.methods,
static_methods: self.static_methods,
}
}
}
#[derive(Debug)]
pub struct StructExport<C> {
base: Option<&'static str>,
static_methods: &'static [GlobalMetadata],
_phantom: PhantomData<fn() -> C>,
}
impl<C> StructExport<C> {
pub fn builder() -> StructExportBuilder<C> {
StructExportBuilder {
base: None,
static_methods: &[],
_phantom: PhantomData,
}
}
}
#[sealed]
impl<C: Default + Clone + NativeRepr> Exportable for StructExport<C> {
fn register(&self) {
let mut rtti = RttiSystemMut::get();
let name_cstr = CString::new(C::NAME).expect("name should be valid");
let base = self
.base
.map(|base| &*rtti.get_class(CName::new(base)).expect("base should exist"));
let handle = NativeClass::<C>::new_handle(&name_cstr, base);
rtti.register_class(handle);
}
fn post_register(&self) {
let converted_static_methods = {
let rtti_ro = RttiSystem::get();
let class = rtti_ro
.get_class(CName::new(C::NAME))
.expect("class should exist");
self.static_methods
.iter()
.map(|m| m.to_rtti_static_method(class))
.collect::<Vec<_>>()
};
let mut rtti_rw = RttiSystemMut::get();
let class = rtti_rw
.get_class(CName::new(C::NAME))
.expect("class should exist");
for static_method in converted_static_methods {
class.add_static_method(static_method);
}
}
}
#[derive(Debug)]
pub struct StructExportBuilder<C> {
base: Option<&'static str>,
static_methods: &'static [GlobalMetadata],
_phantom: PhantomData<fn() -> C>,
}
impl<C> StructExportBuilder<C> {
pub const fn base(mut self, base: &'static str) -> Self {
self.base = Some(base);
self
}
pub const fn static_methods(mut self, static_methods: &'static [GlobalMetadata]) -> Self {
self.static_methods = static_methods;
self
}
pub const fn build(self) -> StructExport<C> {
StructExport {
base: self.base,
static_methods: self.static_methods,
_phantom: PhantomData,
}
}
}
#[derive(Debug)]
pub struct GlobalExport(pub GlobalMetadata);
#[sealed]
impl Exportable for GlobalExport {
#[inline]
fn register(&self) {}
fn post_register(&self) {
let converted = self.0.to_rtti();
let mut rtti = RttiSystemMut::get();
rtti.register_function(converted);
}
}
#[macro_export]
macro_rules! exports {
[$export:expr, $($tt:tt)*] => {
$crate::ExportList::new($export, exports!($($tt)*))
};
[$export:expr] => {
$crate::ExportList::new($export, $crate::ExportNil)
};
[] => { $crate::ExportNil }
}
#[macro_export]
macro_rules! methods {
[$( $($mod:ident)* $name:literal => $ty:ident::$id:ident),*$(,)?] => {
const { &[$($crate::method!($($mod)* $name, $ty::$id)),*] }
};
}
#[macro_export]
macro_rules! static_methods {
[$( $($mod:ident)* $name:literal => $ty:ident::$id:ident),*$(,)?] => {
const { &[$($crate::global!($($mod)* $name, $ty::$id)),*] }
};
}