1
1
use super :: { from:: FromScript , into:: IntoScript , namespace:: Namespace } ;
2
+ use crate :: bindings:: function:: arg_info:: ArgInfo ;
2
3
use crate :: {
3
4
bindings:: {
4
5
function:: from:: { Mut , Ref , Val } ,
@@ -9,20 +10,17 @@ use crate::{
9
10
} ;
10
11
use bevy:: {
11
12
prelude:: { Reflect , Resource } ,
12
- reflect:: {
13
- func:: { args:: GetOwnership , FunctionError } ,
14
- FromReflect , GetTypeRegistration , TypePath , TypeRegistry , Typed ,
15
- } ,
13
+ reflect:: { func:: FunctionError , FromReflect , GetTypeRegistration , TypeRegistry , Typed } ,
16
14
} ;
17
15
use parking_lot:: { RwLock , RwLockReadGuard , RwLockWriteGuard } ;
18
16
use std:: borrow:: Cow ;
19
- use std:: collections:: HashMap ;
17
+ use std:: collections:: { HashMap , VecDeque } ;
20
18
use std:: hash:: Hash ;
21
19
use std:: ops:: { Deref , DerefMut } ;
22
20
use std:: sync:: Arc ;
23
21
24
22
#[ diagnostic:: on_unimplemented(
25
- message = "Only functions with all arguments impplementing FromScript and return values supporting IntoScript are supported. Registering functions also requires they implement GetInnerTypeDependencies " ,
23
+ message = "This function does not fulfil the requirements to be a script callable function. All arguments must implement the ScriptArgument trait and all return values must implement the ScriptReturn trait " ,
26
24
note = "If you're trying to return a non-primitive type, you might need to use Val<T> Ref<T> or Mut<T> wrappers"
27
25
) ]
28
26
pub trait ScriptFunction < ' env , Marker > {
@@ -159,7 +157,9 @@ impl FunctionInfo {
159
157
pub struct DynamicScriptFunction {
160
158
pub info : FunctionInfo ,
161
159
// TODO: info about the function, this is hard right now because of non 'static lifetimes in wrappers, we can't use TypePath etc
162
- func : Arc < dyn Fn ( FunctionCallContext , Vec < ScriptValue > ) -> ScriptValue + Send + Sync + ' static > ,
160
+ func : Arc <
161
+ dyn Fn ( FunctionCallContext , VecDeque < ScriptValue > ) -> ScriptValue + Send + Sync + ' static ,
162
+ > ,
163
163
}
164
164
165
165
impl PartialEq for DynamicScriptFunction {
@@ -175,7 +175,10 @@ pub struct DynamicScriptFunctionMut {
175
175
func : Arc <
176
176
RwLock <
177
177
// I'd rather consume an option or something instead of having the RWLock but I just wanna get this release out
178
- dyn FnMut ( FunctionCallContext , Vec < ScriptValue > ) -> ScriptValue + Send + Sync + ' static ,
178
+ dyn FnMut ( FunctionCallContext , VecDeque < ScriptValue > ) -> ScriptValue
179
+ + Send
180
+ + Sync
181
+ + ' static ,
179
182
> ,
180
183
> ,
181
184
}
@@ -195,7 +198,7 @@ impl DynamicScriptFunction {
195
198
args : I ,
196
199
context : FunctionCallContext ,
197
200
) -> Result < ScriptValue , InteropError > {
198
- let args = args. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
201
+ let args = args. into_iter ( ) . collect :: < VecDeque < _ > > ( ) ;
199
202
// should we be inlining call errors into the return value?
200
203
let return_val = ( self . func ) ( context, args) ;
201
204
match return_val {
@@ -242,7 +245,7 @@ impl DynamicScriptFunctionMut {
242
245
args : I ,
243
246
context : FunctionCallContext ,
244
247
) -> Result < ScriptValue , InteropError > {
245
- let args = args. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
248
+ let args = args. into_iter ( ) . collect :: < VecDeque < _ > > ( ) ;
246
249
// should we be inlining call errors into the return value?
247
250
let mut write = self . func . write ( ) ;
248
251
let return_val = ( write) ( context, args) ;
@@ -298,7 +301,7 @@ impl std::fmt::Debug for DynamicScriptFunctionMut {
298
301
299
302
impl < F > From < F > for DynamicScriptFunction
300
303
where
301
- F : Fn ( FunctionCallContext , Vec < ScriptValue > ) -> ScriptValue + Send + Sync + ' static ,
304
+ F : Fn ( FunctionCallContext , VecDeque < ScriptValue > ) -> ScriptValue + Send + Sync + ' static ,
302
305
{
303
306
fn from ( fn_ : F ) -> Self {
304
307
DynamicScriptFunction {
@@ -311,7 +314,7 @@ where
311
314
312
315
impl < F > From < F > for DynamicScriptFunctionMut
313
316
where
314
- F : FnMut ( FunctionCallContext , Vec < ScriptValue > ) -> ScriptValue + Send + Sync + ' static ,
317
+ F : FnMut ( FunctionCallContext , VecDeque < ScriptValue > ) -> ScriptValue + Send + Sync + ' static ,
315
318
{
316
319
fn from ( fn_ : F ) -> Self {
317
320
DynamicScriptFunctionMut {
@@ -562,39 +565,50 @@ macro_rules! impl_script_function {
562
565
impl <
563
566
' env,
564
567
' w : ' static ,
565
- $( $param: FromScript , ) *
568
+ $( $param: FromScript + ArgInfo , ) *
566
569
O ,
567
570
F
568
571
> $trait_type<' env,
569
572
fn ( $( $contextty, ) ? $( $param ) ,* ) -> $res
570
573
> for F
571
574
where
572
- O : IntoScript + TypePath + GetOwnership ,
575
+ O : IntoScript ,
573
576
F : $fn_type( $( $contextty, ) ? $( $param ) ,* ) -> $res + Send + Sync + ' static ,
574
577
$( $param:: This <' w>: Into <$param>, ) *
575
578
{
576
579
#[ allow( unused_mut, unused_variables) ]
577
580
fn $trait_fn_name( mut self ) -> $dynamic_type {
578
- let func = ( move |caller_context: FunctionCallContext , args: Vec <ScriptValue > | {
581
+ let func = ( move |caller_context: FunctionCallContext , mut args: VecDeque <ScriptValue > | {
579
582
let res: Result <ScriptValue , InteropError > = ( || {
583
+ let received_args_len = args. len( ) ;
580
584
let expected_arg_count = count!( $( $param ) * ) ;
581
- if args. len( ) < expected_arg_count {
582
- return Err ( InteropError :: function_call_error( FunctionError :: ArgCountMismatch {
583
- expected: expected_arg_count,
584
- received: args. len( )
585
- } ) ) ;
586
- }
585
+
587
586
$( let $context = caller_context; ) ?
588
587
let world = caller_context. world( ) ?;
589
588
world. begin_access_scope( ) ?;
589
+ let mut current_arg = 0 ;
590
+
591
+ $(
592
+ current_arg += 1 ;
593
+ let $param = args. pop_front( ) ;
594
+ let $param = match $param {
595
+ Some ( $param) => $param,
596
+ None => {
597
+ if let Some ( default ) = <$param>:: default_value( ) {
598
+ default
599
+ } else {
600
+ return Err ( InteropError :: function_call_error( FunctionError :: ArgCountMismatch {
601
+ expected: expected_arg_count,
602
+ received: received_args_len
603
+ } ) ) ;
604
+ }
605
+ }
606
+ } ;
607
+ let $param = <$param>:: from_script( $param, world. clone( ) )
608
+ . map_err( |e| InteropError :: function_arg_conversion_error( current_arg. to_string( ) , e) ) ?;
609
+ ) *
610
+
590
611
let ret = {
591
- let mut current_arg = 0 ;
592
- let mut arg_iter = args. into_iter( ) ;
593
- $(
594
- current_arg += 1 ;
595
- let $param = <$param>:: from_script( arg_iter. next( ) . expect( "invariant" ) , world. clone( ) )
596
- . map_err( |e| InteropError :: function_arg_conversion_error( current_arg. to_string( ) , e) ) ?;
597
- ) *
598
612
let out = self ( $( $context, ) ? $( $param. into( ) , ) * ) ;
599
613
$(
600
614
let $out = out?;
@@ -638,10 +652,20 @@ bevy::utils::all_tuples!(impl_script_function_type_dependencies, 0, 13, T);
638
652
#[ cfg( test) ]
639
653
mod test {
640
654
use super :: * ;
655
+
656
+ fn with_local_world < F : Fn ( ) > ( f : F ) {
657
+ let mut world = bevy:: prelude:: World :: default ( ) ;
658
+ WorldGuard :: with_static_guard ( & mut world, |world| {
659
+ ThreadWorldContainer . set_world ( world) . unwrap ( ) ;
660
+ f ( )
661
+ } ) ;
662
+ }
663
+
641
664
#[ test]
642
665
fn test_register_script_function ( ) {
643
666
let mut registry = ScriptFunctionRegistry :: default ( ) ;
644
667
let fn_ = |a : usize , b : usize | a + b;
668
+
645
669
let namespace = Namespace :: Global ;
646
670
registry. register ( namespace, "test" , fn_) ;
647
671
let function = registry
@@ -652,6 +676,46 @@ mod test {
652
676
assert_eq ! ( function. info. namespace( ) , namespace) ;
653
677
}
654
678
679
+ #[ test]
680
+ fn test_optional_argument_not_required ( ) {
681
+ let fn_ = |a : usize , b : Option < usize > | a + b. unwrap_or ( 0 ) ;
682
+ let script_function = fn_. into_dynamic_script_function ( ) ;
683
+
684
+ with_local_world ( || {
685
+ let out = script_function
686
+ . call ( vec ! [ ScriptValue :: from( 1 ) ] , FunctionCallContext :: default ( ) )
687
+ . unwrap ( ) ;
688
+
689
+ assert_eq ! ( out, ScriptValue :: from( 1 ) ) ;
690
+ } ) ;
691
+ }
692
+
693
+ #[ test]
694
+ fn test_invalid_amount_of_args_errors_nicely ( ) {
695
+ let fn_ = |a : usize , b : usize | a + b;
696
+ let script_function = fn_. into_dynamic_script_function ( ) . with_name ( "my_fn" ) ;
697
+
698
+ with_local_world ( || {
699
+ let out =
700
+ script_function. call ( vec ! [ ScriptValue :: from( 1 ) ] , FunctionCallContext :: default ( ) ) ;
701
+
702
+ assert ! ( out. is_err( ) ) ;
703
+ assert_eq ! (
704
+ out. unwrap_err( ) . into_inner( ) . unwrap( ) ,
705
+ InteropError :: function_interop_error(
706
+ "my_fn" ,
707
+ Namespace :: Global ,
708
+ InteropError :: function_call_error( FunctionError :: ArgCountMismatch {
709
+ expected: 2 ,
710
+ received: 1
711
+ } )
712
+ )
713
+ . into_inner( )
714
+ . unwrap( )
715
+ ) ;
716
+ } ) ;
717
+ }
718
+
655
719
#[ test]
656
720
fn test_overloaded_script_function ( ) {
657
721
let mut registry = ScriptFunctionRegistry :: default ( ) ;
0 commit comments