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