Skip to main content

rrplug/high/
squirrel_traits.rs

1//! trait definitions and implementations to generalize interacting with squirrel
2
3#![allow(clippy::not_unsafe_ptr_arg_deref)] // maybe remove this later
4
5pub use rrplug_proc::{GetFromSQObject, GetFromSquirrelVm, PushToSquirrelVm, SQVMName};
6use std::{mem::MaybeUninit, ptr::NonNull};
7
8use crate::{
9    bindings::{
10        server::{cbaseentity::CBaseEntity, cplayer::CPlayer},
11        squirrelclasstypes::SQRESULT,
12        squirreldatatypes::{
13            SQArray, SQBool, SQClosure, SQFloat, SQFunctionProto, SQInteger, SQNativeClosure,
14            SQObject, SQObjectType, SQObjectValue, SQString, SQStructInstance, SQTable,
15        },
16    },
17    high::squirrel::SQHandle,
18    mid::{
19        squirrel::{
20            get_sq_array, get_sq_bool, get_sq_float, get_sq_int, get_sq_object, get_sq_string,
21            get_sq_vector, push_sq_array, push_sq_bool, push_sq_float, push_sq_int, push_sq_object,
22            push_sq_string, push_sq_vector, sqvm_to_context,
23        },
24        utils::to_cstring,
25    },
26    prelude::*,
27};
28
29use super::UnsafeHandle;
30
31// Push Trait
32
33macro_rules! push_to_sqvm {
34    ( $( $function:ident::<$t:ty> );*; ) => { $(
35
36        impl PushToSquirrelVm for $t {
37            #[inline]
38            fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) {
39                $function(sqvm, sqfunctions, self)
40            }
41        }
42    )* }
43}
44
45/// trait to used to generalize pushing to the sq stack
46///
47/// # Use cases
48/// - returning from native functions
49/// - accumulating in arrays and structs
50pub trait PushToSquirrelVm {
51    /// used for ()
52    #[doc(hidden)]
53    const DEFAULT_RESULT: SQRESULT = SQRESULT::SQRESULT_NOTNULL;
54
55    /// pushes the value to the stack
56    fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions);
57}
58
59push_to_sqvm! {
60    push_sq_string::<String>;
61    push_sq_string::<&str>;
62    push_sq_int::<i32>;
63    push_sq_float::<f32>;
64    push_sq_bool::<bool>;
65    push_sq_vector::<Vector3>;
66    push_sq_object::<SQObject>;
67}
68
69impl<T> PushToSquirrelVm for Vec<T>
70where
71    T: PushToSquirrelVm,
72{
73    fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) {
74        push_sq_array(sqvm, sqfunctions, self);
75    }
76}
77
78impl PushToSquirrelVm for () {
79    const DEFAULT_RESULT: SQRESULT = SQRESULT::SQRESULT_NULL;
80
81    #[inline]
82    fn push_to_sqvm(self, _: NonNull<HSquirrelVM>, _: &SquirrelFunctions) {}
83}
84
85impl PushToSquirrelVm for &CPlayer {
86    /// SAFETY: the object is stored inside the entity and the entity is not being modified  
87    fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) {
88        unsafe {
89            let obj =
90                (sqfunctions.sq_create_script_instance)((self as *const CPlayer).cast_mut().cast());
91            (sqfunctions.sq_pushobject)(sqvm.as_ptr(), obj);
92        }
93    }
94}
95
96impl PushToSquirrelVm for &CBaseEntity {
97    /// SAFETY: the object is stored inside the entity and the entity is not being modified  
98    fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) {
99        unsafe {
100            let obj = (sqfunctions.sq_create_script_instance)(
101                (self as *const CBaseEntity).cast_mut().cast(),
102            );
103            (sqfunctions.sq_pushobject)(sqvm.as_ptr(), obj);
104        }
105    }
106}
107
108impl<T: PushToSquirrelVm, const N: usize> PushToSquirrelVm for [T; N] {
109    fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) {
110        push_sq_array(sqvm, sqfunctions, self);
111    }
112}
113
114// hmm maybe bad idea but eh (looking at the copy stuff)
115impl<T: PushToSquirrelVm + Clone + Copy> PushToSquirrelVm for &[T] {
116    fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) {
117        push_sq_array(sqvm, sqfunctions, self.iter().copied());
118    }
119}
120
121impl<T: PushToSquirrelVm> PushToSquirrelVm for UnsafeHandle<T>
122where
123    T: PushToSquirrelVm,
124{
125    fn push_to_sqvm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) {
126        self.take().push_to_sqvm(sqvm, sqfunctions)
127    }
128}
129
130// Return Trait
131
132/// trait to return diffrent values to the sqvm from a native closure
133///
134/// [`Option`] will return a `ornull` type in squirrel
135///
136/// [`Result`] will return its T type in squirrel and will raise an exception in squirrel if it's an error
137/// # Use cases
138/// - returning from native functions
139pub trait ReturnToVm {
140    /// returns a value defined by [`SQRESULT`]
141    fn return_to_vm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) -> SQRESULT;
142}
143
144impl<T: PushToSquirrelVm> ReturnToVm for Option<T> {
145    /// returns a `ornull T` to the sqvm
146    fn return_to_vm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) -> SQRESULT {
147        match self {
148            Some(rtrn) => {
149                rtrn.push_to_sqvm(sqvm, sqfunctions);
150                T::DEFAULT_RESULT
151            }
152            None => {
153                unsafe { (sqfunctions.sq_pushnull)(sqvm.as_ptr()) };
154                SQRESULT::SQRESULT_NULL
155            }
156        }
157    }
158}
159
160impl<T: PushToSquirrelVm, E: ToString> ReturnToVm for Result<T, E> {
161    /// will raise a squirrel exception if it's an error
162    ///
163    /// result returns of T,R are identical to non result returns of T
164    fn return_to_vm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) -> SQRESULT {
165        match self {
166            Ok(rtrn) => {
167                rtrn.push_to_sqvm(sqvm, sqfunctions);
168                T::DEFAULT_RESULT
169            }
170            Err(err) => {
171                let err = to_cstring(err.to_string().as_str());
172                unsafe { (sqfunctions.sq_raiseerror)(sqvm.as_ptr(), err.as_ptr()) };
173                SQRESULT::SQRESULT_ERROR
174            }
175        }
176    }
177}
178
179impl<T: PushToSquirrelVm> ReturnToVm for T {
180    /// any return for types simply pushes it and returns NonNull
181    fn return_to_vm(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &SquirrelFunctions) -> SQRESULT {
182        self.push_to_sqvm(sqvm, sqfunctions);
183        T::DEFAULT_RESULT
184    }
185}
186
187// IntoSquirrelArgs Trait
188
189/// closure that simplies pushing groups of items to the squirrel vm; asynchronously or immediately
190pub trait IntoSquirrelArgs {
191    /// converts a implemenator of this trait into a closure that pushes it to the squirrel stack when ran
192    fn into_function(
193        self,
194    ) -> Box<
195        dyn FnOnce(NonNull<HSquirrelVM>, &'static SquirrelFunctions) -> i32 + 'static + Send + Sync,
196    >
197    where
198        Self: Sized + Send + Sync + 'static,
199    {
200        Box::new(
201            move |sqvm: NonNull<HSquirrelVM>, sqfunctions: &'static SquirrelFunctions| {
202                self.into_push(sqvm, sqfunctions)
203            },
204        )
205    }
206
207    /// pushes the args to the sqvm and returns the amount pushed
208    fn into_push(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &'static SquirrelFunctions) -> i32;
209}
210
211impl<T: PushToSquirrelVm> IntoSquirrelArgs for T {
212    fn into_push(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &'static SquirrelFunctions) -> i32 {
213        // hack :(
214        // no specialization
215        if T::DEFAULT_RESULT != SQRESULT::SQRESULT_NULL {
216            self.push_to_sqvm(sqvm, sqfunctions);
217            1
218        } else {
219            0
220        }
221    }
222}
223
224// TODO: check for correctness
225macro_rules! into_squirrel_args_impl{
226    ( $( ($($ty_name: ident : $tuple_index:tt),*) );*; ) => { $(
227        impl<$($ty_name: PushToSquirrelVm,)*> IntoSquirrelArgs for ($($ty_name,)*) {
228            fn into_push(self, sqvm: NonNull<HSquirrelVM>, sqfunctions: &'static SquirrelFunctions) -> i32 {
229                $(
230                    self.$tuple_index.push_to_sqvm(sqvm, sqfunctions);
231                )*
232                $crate::macros::sq_utils::__arg_count_helper([$($crate::__replace_expr!($ty_name)),*]) as i32
233            }
234        }
235    )* }
236}
237
238into_squirrel_args_impl! {
239    (T1: 0);
240    (T1: 0, T2: 1);
241    (T1: 0, T2: 1, T3: 2);
242    (T1: 0, T2: 1, T3: 2, T4: 3);
243    (T1: 0, T2: 1, T3: 2, T4: 3, T5: 4);
244    (T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5);
245    (T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6);
246    (T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6, T8: 7);
247    (T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6, T8: 7, T9: 8);
248    (T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6, T8: 7, T9: 8, T10: 9);
249}
250
251// Get Trait
252
253macro_rules! get_from_sqvm {
254    ( $( $function:ident::<$t:ty> );*; ) => { $(
255
256        impl GetFromSquirrelVm for $t {
257            #[inline]
258            fn get_from_sqvm(
259                sqvm: NonNull<HSquirrelVM>,
260                sqfunctions: &SquirrelFunctions,
261                stack_pos: i32,
262            ) -> Self {
263                $function(sqvm, sqfunctions, stack_pos)
264            }
265        }
266    )* };
267
268    ( $( ($($ty_name: ident : $var_name:ident),*) );*; ) => { $(
269        impl<$($ty_name: PushToSquirrelVm,)*> GetFromSquirrelVm for Box<dyn Fn($($ty_name,)*)> {
270            fn get_from_sqvm(
271                sqvm: NonNull<HSquirrelVM>,
272                sqfunctions: &'static SquirrelFunctions,
273                stack_pos: i32,
274            ) -> Self {
275                Box::new(move |$($var_name: $ty_name,)*| { _ =
276                    call_sq_object_function!(
277                        sqvm,
278                        sqfunctions,
279                        SQHandle::<SQClosure>::get_from_sqvm(sqvm, sqfunctions, stack_pos),
280                        $($var_name),*
281                    );
282                })
283            }
284        }
285    )* }
286}
287
288/// trait to get values out of the squrriel stack
289///
290/// # Use cases
291/// - getting the arguments in native closures
292pub trait GetFromSquirrelVm: Sized {
293    /// tries to get the value out of the squirrel stack but it cannot fail
294    /// so this can panic
295    ///
296    /// this is the user function do not overwrite the other one
297    fn get_from_sqvm(
298        sqvm: NonNull<HSquirrelVM>,
299        sqfunctions: &'static SquirrelFunctions,
300        stack_pos: i32,
301    ) -> Self;
302
303    /// this is only for certain internal apis
304    ///
305    /// don't use this only for internal apis
306    #[doc(hidden)]
307    #[inline]
308    fn get_from_sqvm_internal(
309        sqvm: NonNull<HSquirrelVM>,
310        sqfunctions: &'static SquirrelFunctions,
311        stack_pos: &mut i32,
312    ) -> Self {
313        let s = Self::get_from_sqvm(sqvm, sqfunctions, *stack_pos);
314
315        // increament by the size this thing in the stack
316        // for some reason userdata also has a table pushed with it; quite annoying
317        *stack_pos += 1;
318
319        s
320    }
321}
322
323get_from_sqvm! {
324    get_sq_string::<String>;
325    get_sq_int::<i32>;
326    get_sq_float::<f32>;
327    get_sq_bool::<bool>;
328    get_sq_vector::<Vector3>;
329    get_sq_object::<SQObject>;
330}
331
332impl<T> GetFromSquirrelVm for Vec<T>
333where
334    T: GetFromSQObject,
335{
336    fn get_from_sqvm(
337        sqvm: NonNull<HSquirrelVM>,
338        _: &'static SquirrelFunctions,
339        stack_pos: i32,
340    ) -> Self {
341        get_sq_array(sqvm, stack_pos)
342    }
343}
344
345impl GetFromSquirrelVm for Option<&mut CPlayer> {
346    fn get_from_sqvm(
347        mut sqvm: NonNull<HSquirrelVM>,
348        sqfunctions: &SquirrelFunctions,
349        stack_pos: i32,
350    ) -> Self {
351        unsafe {
352            debug_assert_eq!(
353                sqvm_to_context(sqvm),
354                ScriptContext::SERVER,
355                "CPlayer only exists on server vm use C_Player for CLIENT and UI"
356            );
357
358            let sqvm = sqvm.as_mut();
359            let cs_sqvm = sqvm
360                .sharedState
361                .as_ref()
362                .expect("shared state was invalid")
363                .cSquirrelVM;
364
365            let mut obj = MaybeUninit::<SQObject>::uninit();
366            (sqfunctions.sq_getobject)(sqvm, stack_pos, obj.as_mut_ptr());
367
368            (sqfunctions.sq_getentityfrominstance)(
369                cs_sqvm,
370                obj.as_mut_ptr(),
371                (sqfunctions.sq_get_entity_constant_cbase_entity)(),
372            )
373            .cast::<CBaseEntity>()
374            .as_mut()?
375            .dynamic_cast_mut()
376        }
377    }
378}
379
380impl GetFromSquirrelVm for Option<&mut CBaseEntity> {
381    fn get_from_sqvm(
382        mut sqvm: NonNull<HSquirrelVM>,
383        sqfunctions: &SquirrelFunctions,
384        stack_pos: i32,
385    ) -> Self {
386        unsafe {
387            debug_assert_eq!(
388                sqvm_to_context(sqvm),
389                ScriptContext::SERVER,
390                "CBaseEnity only exists on server vm"
391            );
392
393            let sqvm = sqvm.as_mut();
394            let cs_sqvm = sqvm
395                .sharedState
396                .as_ref()
397                .expect("shared state was invalid")
398                .cSquirrelVM;
399
400            let mut obj = MaybeUninit::<SQObject>::uninit();
401            (sqfunctions.sq_getobject)(sqvm, stack_pos, obj.as_mut_ptr());
402
403            let ent = (sqfunctions.sq_getentityfrominstance)(
404                cs_sqvm,
405                obj.as_mut_ptr(),
406                (sqfunctions.sq_get_entity_constant_cbase_entity)(),
407            )
408            .cast::<CBaseEntity>()
409            .as_mut()?;
410
411            Some(ent)
412        }
413    }
414}
415
416impl<'a, T: IsSQObject<'a>> GetFromSquirrelVm for SQHandle<'a, T> {
417    fn get_from_sqvm(
418        sqvm: NonNull<HSquirrelVM>,
419        sqfunctions: &SquirrelFunctions,
420        stack_pos: i32,
421    ) -> Self {
422        unsafe {
423            let mut obj = std::mem::MaybeUninit::<SQObject>::uninit();
424            (sqfunctions.sq_getobject)(sqvm.as_ptr(), stack_pos, obj.as_mut_ptr());
425
426            match Self::try_new(obj.assume_init()) {
427                Ok(handle) => handle,
428                Err(_) => {
429                    panic!(
430                        "the object wasn't the correct type got {:X} expected {}",
431                        obj.assume_init()._Type as i32,
432                        std::any::type_name::<T>()
433                    );
434                }
435            }
436        }
437    }
438}
439
440impl<T: IntoSquirrelArgs> GetFromSquirrelVm for SquirrelFn<'_, T> {
441    #[inline]
442    fn get_from_sqvm(
443        sqvm: NonNull<HSquirrelVM>,
444        sqfunctions: &'static SquirrelFunctions,
445        stack_pos: i32,
446    ) -> Self {
447        SquirrelFn {
448            func: GetFromSquirrelVm::get_from_sqvm(sqvm, sqfunctions, stack_pos),
449            phantom: std::marker::PhantomData,
450        }
451    }
452}
453
454impl GetFromSquirrelVm for () {
455    /// exists for dynamic returns of some functions
456    fn get_from_sqvm(_: NonNull<HSquirrelVM>, _: &SquirrelFunctions, _: i32) -> Self {}
457}
458
459// Get From SQObject Trait
460
461/// gets the value out of a sqobject
462///
463/// most implementations don't check the type
464///
465/// so this can panic if it's not the correct type
466///
467/// # Use cases
468/// - getting fields of arrays and structs
469pub trait GetFromSQObject {
470    /// gets the value out of a sqobject
471    ///
472    /// halts if the type is incorrect
473    fn get_from_sqobject(obj: &SQObject) -> Self;
474}
475
476impl GetFromSQObject for () {
477    #[inline]
478    fn get_from_sqobject(_: &SQObject) -> Self {}
479}
480
481impl GetFromSQObject for String {
482    #[inline]
483    fn get_from_sqobject(obj: &SQObject) -> Self {
484        unsafe {
485            std::ffi::CStr::from_ptr(
486                (&obj._VAL.asString.as_ref().unwrap_unchecked()._val) as *const i8,
487            )
488            .to_string_lossy()
489            .into()
490        }
491    }
492}
493
494impl GetFromSQObject for i32 {
495    #[inline]
496    fn get_from_sqobject(obj: &SQObject) -> Self {
497        unsafe { obj._VAL.asInteger }
498    }
499}
500
501impl GetFromSQObject for f32 {
502    #[inline]
503    fn get_from_sqobject(obj: &SQObject) -> Self {
504        unsafe { obj._VAL.asFloat }
505    }
506}
507
508impl GetFromSQObject for bool {
509    #[inline]
510    fn get_from_sqobject(obj: &SQObject) -> Self {
511        unsafe { obj._VAL.asInteger != 0 }
512    }
513}
514
515impl GetFromSQObject for Vector3 {
516    #[inline]
517    fn get_from_sqobject(obj: &SQObject) -> Self {
518        (obj as *const SQObject).into()
519    }
520}
521
522impl GetFromSQObject for SQObject {
523    #[inline]
524    fn get_from_sqobject(obj: &SQObject) -> Self {
525        *obj
526    }
527}
528
529impl<'a, T: IsSQObject<'a>> GetFromSQObject for SQHandle<'a, T> {
530    #[inline]
531    fn get_from_sqobject(obj: &SQObject) -> Self {
532        match Self::try_new(*obj) {
533            Ok(handle) => handle,
534            Err(_) => {
535                panic!(
536                    "the object wasn't the correct type got {:X} expected {}",
537                    obj._Type as i32,
538                    std::any::type_name::<T>()
539                );
540            }
541        }
542    }
543}
544
545impl<T: IntoSquirrelArgs> GetFromSQObject for SquirrelFn<'_, T> {
546    #[inline]
547    fn get_from_sqobject(obj: &SQObject) -> Self {
548        SquirrelFn {
549            func: SQHandle::try_new(obj.to_owned())
550                .expect("the squirrel object wasn't a function lol L"),
551            phantom: std::marker::PhantomData,
552        }
553    }
554}
555
556impl<T> GetFromSQObject for Vec<T>
557where
558    T: GetFromSQObject,
559{
560    #[inline]
561    fn get_from_sqobject(obj: &SQObject) -> Self {
562        unsafe {
563            let array = obj
564                ._VAL
565                .asArray
566                .as_ref()
567                .expect("the sq object may be invalid");
568
569            (0..array._usedSlots as usize)
570                .map(|i| array._values.add(i))
571                .filter_map(|obj| obj.as_ref())
572                .map(T::get_from_sqobject)
573                .collect()
574        }
575    }
576}
577
578// sqvm name
579
580macro_rules! sqvm_name {
581    ($( ($($ty_name:ident : $var_name:ident),*) );*;)  => {
582        $(
583            impl<$($ty_name: SQVMName,)*> SQVMName for ($($ty_name,)*) {
584                fn get_sqvm_name() -> String {
585                    let mut name = String::new();
586
587                    $(
588                        if !name.is_empty() { // bad solution but this will run only once for each use
589                            name.push(',');
590                            name.push(' ');
591                        }
592                        name.push_str(&$ty_name::get_sqvm_name());
593                    )*
594
595                    name
596                }
597            }
598        )*
599    };
600
601    ( $( $t:ty = $sqty:literal );*; ) => {
602        $(
603            impl SQVMName for $t {
604                #[inline]
605                fn get_sqvm_name() -> String {
606                     $sqty.to_string()
607                }
608            }
609        )*
610    };
611
612    ( $( LIFE $t:ty = $sqty:literal );*; ) => {
613        $(
614            impl<'a> SQVMName for $t {
615                #[inline]
616                fn get_sqvm_name() -> String {
617                     $sqty.to_string()
618                }
619            }
620        )*
621    };
622}
623
624/// the sqvm name of a type in rust
625///
626/// used to map a rust function into a sq native function
627///
628/// # Use cases
629/// - translating rust types to squirrel types
630pub trait SQVMName {
631    /// the name on the sqvm of a type
632    ///
633    /// the default is "var" which is any type
634    fn get_sqvm_name() -> String;
635}
636
637sqvm_name! {
638    String = "string";
639    &str = "string";
640    i32 = "int";
641    f32 = "float";
642    bool = "bool";
643    Vector3 = "vector";
644    Option<&mut CPlayer> = "entity";
645    Option<&mut CBaseEntity> = "entity";
646    Option<&CPlayer> = "entity";
647    Option<&CBaseEntity> = "entity";
648    SQObject = "var";
649    () = "void";
650}
651
652sqvm_name! {
653    LIFE SQHandle<'a, SQClosure> = "var";
654    LIFE SQHandle<'a, SQTable> = "table";
655    LIFE SQHandle<'a, SQString> = "string";
656    LIFE SQHandle<'a, SQArray> = "array";
657    LIFE SQHandle<'a, SQFloat> = "float";
658    LIFE SQHandle<'a, SQInteger> = "int";
659    LIFE SQHandle<'a, SQFunctionProto> = "var";
660    LIFE SQHandle<'a, SQStructInstance> = "var";
661    LIFE SQHandle<'a, SQBool> = "bool";
662    LIFE SQHandle<'a, SQNativeClosure> = "var";
663}
664
665sqvm_name! {
666    (T1: v2);
667    (T1: v1, T2: v2);
668    (T1: v1, T2: v2, T3: v3);
669    (T1: v1, T2: v2, T3: v3, T4: v4);
670    (T1: v1, T2: v2, T3: v3, T4: v4, T5: v5);
671    (T1: v1, T2: v2, T3: v3, T4: v4, T5: v5, T6: v6);
672    (T1: v1, T2: v2, T3: v3, T4: v4, T5: v5, T6: v6, T7: v7);
673    (T1: v1, T2: v2, T3: v3, T4: v4, T5: v5, T6: v6, T7: v7, T8: v8);
674    (T1: v1, T2: v2, T3: v3, T4: v4, T5: v5, T6: v6, T7: v7, T8: v8, T9: v9);
675    (T1: v1, T2: v2, T3: v3, T4: v4, T5: v5, T6: v6, T7: v7, T8: v8, T9: v9, T10: v10);
676}
677
678impl<T: SQVMName + IntoSquirrelArgs> SQVMName for SquirrelFn<'_, T> {
679    fn get_sqvm_name() -> String {
680        format!("void functionref({})", T::get_sqvm_name())
681    }
682}
683
684impl<T: SQVMName> SQVMName for Vec<T> {
685    fn get_sqvm_name() -> String {
686        format!("array<{}>", T::get_sqvm_name())
687    }
688}
689
690impl<T: SQVMName, const N: usize> SQVMName for [T; N] {
691    fn get_sqvm_name() -> String {
692        Vec::<T>::get_sqvm_name()
693    }
694}
695
696impl<T: SQVMName> SQVMName for &[T] {
697    fn get_sqvm_name() -> String {
698        Vec::<T>::get_sqvm_name()
699    }
700}
701
702// because of this `void ornull` is possible oops
703impl<T: SQVMName> SQVMName for Option<T> {
704    fn get_sqvm_name() -> String {
705        format!("{} ornull", T::get_sqvm_name())
706    }
707}
708
709impl<T: SQVMName, E> SQVMName for Result<T, E> {
710    fn get_sqvm_name() -> String {
711        T::get_sqvm_name() // yeah squirrel doesn't have a way in the type system to sepecify a possible error :|
712    }
713}
714
715// specialization is not as strong as I though :(
716// impl SQVMName for Option<()> {
717//     fn get_sqvm_name() -> String {
718//         "void".to_string()
719//     }
720// }
721
722// Markers
723
724macro_rules! is_sq_object {
725    ( $( $object:ty,RT: $rt:expr,OT: $ot:expr, EXTRACT: * $extract:ident );*; ) => {
726        $(
727            impl<'a> IsSQObject<'a> for $object {
728                const OT_TYPE: SQObjectType = $ot;
729                const RT_TYPE: SQObjectType = $rt;
730
731                fn extract_mut(val: &'a mut SQObjectValue) -> &'a mut Self {
732                    unsafe { &mut *val.$extract } // asummed to be init
733                }
734
735                fn extract(val: &'a SQObjectValue) -> &'a Self {
736                    unsafe { &*val.$extract } // asummed to be init
737                }
738            }
739        )*
740    };
741
742    ( $( $object:ty,RT: $rt:expr,OT: $ot:expr, EXTRACT: $extract:ident );*; ) => {
743        $(
744            impl<'a> IsSQObject<'a> for $object {
745                const OT_TYPE: SQObjectType = $ot;
746                const RT_TYPE: SQObjectType = $rt;
747
748                fn extract_mut(val: &'a mut SQObjectValue) -> &'a mut Self {
749                    unsafe { std::mem::transmute(&mut val.$extract) } // asummed to be init
750                }
751
752                fn extract(val: &'a SQObjectValue) -> &'a Self {
753                    unsafe { std::mem::transmute(&val.$extract) } // asummed to be init
754                }
755            }
756        )*
757    }
758}
759
760/// trait to define SQObject types
761pub trait IsSQObject<'a> {
762    /// ot type
763    const OT_TYPE: SQObjectType;
764    /// return type
765    const RT_TYPE: SQObjectType;
766
767    /// extracts the `Self` out of the SQObjectValue
768    ///
769    /// this is unsafe if [`SQHandle`] wasn't used
770    fn extract(val: &'a SQObjectValue) -> &'a Self;
771
772    /// extracts the `Self` out of the SQObjectValue
773    ///
774    /// this is unsafe if [`SQHandle`] wasn't used
775    fn extract_mut(val: &'a mut SQObjectValue) -> &'a mut Self;
776}
777
778is_sq_object! {
779    SQTable, RT: SQObjectType::RT_TABLE, OT: SQObjectType::OT_TABLE, EXTRACT: * asTable;
780    SQString, RT: SQObjectType::RT_STRING, OT: SQObjectType::OT_STRING, EXTRACT: * asString;
781    SQFunctionProto, RT: SQObjectType::RT_FUNCPROTO, OT: SQObjectType::OT_FUNCPROTO, EXTRACT: * asFuncProto;
782    SQClosure, RT: SQObjectType::RT_CLOSURE, OT: SQObjectType::OT_CLOSURE, EXTRACT: * asClosure;
783    SQStructInstance, RT: SQObjectType::RT_INSTANCE, OT: SQObjectType::OT_STRUCT, EXTRACT: * asStructInstance;
784    SQNativeClosure, RT: SQObjectType::RT_NATIVECLOSURE, OT: SQObjectType::OT_NATIVECLOSURE, EXTRACT: * asNativeClosure;
785    SQArray, RT: SQObjectType::RT_ARRAY, OT: SQObjectType::OT_ARRAY, EXTRACT: * asArray;
786}
787is_sq_object! {
788    SQFloat, RT: SQObjectType::RT_FLOAT, OT: SQObjectType::OT_FLOAT, EXTRACT: asFloat;
789    SQInteger, RT: SQObjectType::RT_INTEGER, OT: SQObjectType::OT_INTEGER, EXTRACT: asInteger;
790    SQBool, RT: SQObjectType::RT_BOOL, OT: SQObjectType::OT_BOOL, EXTRACT: asInteger;
791} // not a thing? SQStructDef, RT: SQObjectType::, OT: SQObjectType::;
792
793// TODO: so here is the idea
794// have add_sqfunction be generic over extern "C" fn s and have traits to diffrenciate client/server/ui sqfunctions
795// the generic would cover mutitple implementation
796// but with this version the user would have to specifically ask for a sqvm and sqfunctions
797// now that I writing this the biggest problem is the return ...
798// but since it's a int we could have a C struct with a i32 and it would be transparent
799// this would allow the user to return anything that can become that sturct
800// so this is figured out :)
801// also the input could be generic over *mut sqvm
802// but then it would have to be a tuple :pain:
803// maybe a combination of this and proc macro would be better?
804
805// TODO: another thing to think about is the fact that there 5 traits for interacting with the sqvm
806// they are all required for everything so why not just combine most of them into one large trait