Switch to macro_rules!
This commit is contained in:
parent
d842d69be2
commit
aed6fdc4f7
@ -1,8 +1,6 @@
|
||||
pub(crate) use function_impls::impl_function_traits;
|
||||
pub(crate) use reflect_fn::reflect_fn;
|
||||
|
||||
mod from_arg;
|
||||
mod function_impls;
|
||||
mod get_ownership;
|
||||
mod into_return;
|
||||
mod reflect_fn;
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
use bevy_macro_utils::fq_std::FQResult;
|
||||
use proc_macro2::Ident;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{parenthesized, parse_macro_input, token, Block, ExprBlock, Type};
|
||||
use syn::{LitStr, ReturnType, Token};
|
||||
|
||||
struct Arg {
|
||||
mutability: Option<Token![mut]>,
|
||||
name: Ident,
|
||||
_colon: Token![:],
|
||||
ty: Type,
|
||||
}
|
||||
|
||||
impl Parse for Arg {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mutability = if input.peek(Token![mut]) {
|
||||
Some(input.parse()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
mutability,
|
||||
name: input.parse()?,
|
||||
_colon: input.parse()?,
|
||||
ty: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enum FnName {
|
||||
#[expect(
|
||||
dead_code,
|
||||
reason = "for documenting via the type system what `Anon` expects to parse"
|
||||
)]
|
||||
Anon(Token![_]),
|
||||
Lit(LitStr),
|
||||
Name(Ident),
|
||||
Expr(ExprBlock),
|
||||
}
|
||||
|
||||
impl Parse for FnName {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(Token![_]) {
|
||||
Ok(Self::Anon(input.parse()?))
|
||||
} else if lookahead.peek(LitStr) {
|
||||
Ok(Self::Lit(input.parse()?))
|
||||
} else if lookahead.peek(syn::Ident) {
|
||||
Ok(Self::Name(input.parse()?))
|
||||
} else if lookahead.peek(token::Brace) {
|
||||
Ok(Self::Expr(input.parse()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ReflectFn {
|
||||
mutability: Option<Token![mut]>,
|
||||
movability: Option<Token![move]>,
|
||||
_fn_token: Token![fn],
|
||||
name: FnName,
|
||||
_parens: token::Paren,
|
||||
args: Punctuated<Arg, Token![,]>,
|
||||
return_type: ReturnType,
|
||||
body: Block,
|
||||
}
|
||||
|
||||
impl Parse for ReflectFn {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mutability = if input.peek(Token![mut]) {
|
||||
Some(input.parse()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let movability = if input.peek(Token![move]) {
|
||||
Some(input.parse()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let content;
|
||||
Ok(Self {
|
||||
mutability,
|
||||
movability,
|
||||
_fn_token: input.parse()?,
|
||||
name: input.parse()?,
|
||||
_parens: parenthesized!(content in input),
|
||||
args: content.parse_terminated(Arg::parse, Token![,])?,
|
||||
return_type: input.parse()?,
|
||||
body: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reflect_fn(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as ReflectFn);
|
||||
|
||||
let bevy_reflect_path = crate::meta::get_bevy_reflect_path();
|
||||
|
||||
let arg_list = format_ident!("args");
|
||||
|
||||
let dynamic_function = if input.mutability.is_some() {
|
||||
quote! { #bevy_reflect_path::func::DynamicFunctionMut }
|
||||
} else {
|
||||
quote! { #bevy_reflect_path::func::DynamicFunction }
|
||||
};
|
||||
|
||||
let movability = &input.movability;
|
||||
|
||||
let extract_args = input.args.iter().map(|arg| {
|
||||
let mutability = &arg.mutability;
|
||||
let name = &arg.name;
|
||||
let ty = &arg.ty;
|
||||
|
||||
quote! { let #mutability #name = #arg_list.take::<#ty>()?; }
|
||||
});
|
||||
|
||||
let body = &input.body;
|
||||
|
||||
let info = match &input.name {
|
||||
FnName::Anon(_) => quote! { #bevy_reflect_path::func::SignatureInfo::anonymous() },
|
||||
FnName::Lit(name) => {
|
||||
quote! { #bevy_reflect_path::func::SignatureInfo::named(#name) }
|
||||
}
|
||||
FnName::Name(name) => {
|
||||
let name = name.to_string();
|
||||
quote! { #bevy_reflect_path::func::SignatureInfo::named(#name) }
|
||||
}
|
||||
FnName::Expr(expr) => {
|
||||
quote! { #bevy_reflect_path::func::SignatureInfo::named(#expr) }
|
||||
}
|
||||
};
|
||||
|
||||
let arg_info = input.args.iter().enumerate().map(|(index, arg)| {
|
||||
let name = &arg.name;
|
||||
let ty = &arg.ty;
|
||||
|
||||
quote! {
|
||||
#bevy_reflect_path::func::args::ArgInfo::new::<#ty>(#index).with_name(stringify!(#name))
|
||||
}
|
||||
});
|
||||
|
||||
let return_ty = match input.return_type {
|
||||
ReturnType::Default => quote! { () },
|
||||
ReturnType::Type(_, ty) => quote! { #ty },
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(quote! {{
|
||||
#dynamic_function::new(
|
||||
#[allow(unused_mut)]
|
||||
#movability |mut #arg_list| {
|
||||
#(#extract_args)*
|
||||
#FQResult::Ok(#bevy_reflect_path::func::IntoReturn::into_return(#body))
|
||||
},
|
||||
#[allow(unused_braces)]
|
||||
#bevy_reflect_path::func::FunctionInfo::new(
|
||||
#info
|
||||
.with_args(::alloc::vec![#(#arg_info),*])
|
||||
.with_return::<#return_ty>()
|
||||
),
|
||||
)
|
||||
}})
|
||||
}
|
||||
@ -2,7 +2,7 @@ mod assertions;
|
||||
mod common;
|
||||
mod enums;
|
||||
#[cfg(feature = "functions")]
|
||||
pub(crate) mod func;
|
||||
mod func;
|
||||
mod opaque;
|
||||
mod structs;
|
||||
mod tuple_structs;
|
||||
|
||||
@ -826,9 +826,3 @@ pub fn impl_type_path(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
#[proc_macro]
|
||||
pub fn reflect_fn(input: TokenStream) -> TokenStream {
|
||||
impls::func::reflect_fn(input)
|
||||
}
|
||||
|
||||
@ -113,3 +113,296 @@ macro_rules! count_tokens {
|
||||
}
|
||||
|
||||
pub(crate) use count_tokens;
|
||||
|
||||
/// A helper macro for generating instances of [`DynamicFunction`] and [`DynamicFunctionMut`].
|
||||
///
|
||||
/// There are some functions that cannot be automatically converted to a dynamic function
|
||||
/// via [`IntoFunction`] or [`IntoFunctionMut`].
|
||||
/// This normally includes functions with more than 15 arguments and functions that
|
||||
/// return a reference with a lifetime not tied to the first argument.
|
||||
///
|
||||
/// For example, the following fails to compile:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use bevy_reflect::func::IntoFunction;
|
||||
/// fn insert_at(index: usize, value: i32, list: &mut Vec<i32>) -> &i32 {
|
||||
/// list.insert(index, value);
|
||||
/// &list[index]
|
||||
/// }
|
||||
///
|
||||
/// // This will fail to compile since `IntoFunction` expects return values
|
||||
/// // to have a lifetime tied to the first argument, but this function
|
||||
/// // returns a reference tied to the third argument.
|
||||
/// let func = insert_at.into_function();
|
||||
/// ```
|
||||
///
|
||||
/// In these cases, we normally need to generate the [`DynamicFunction`] manually:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{DynamicFunction, IntoFunction, IntoReturn, SignatureInfo};
|
||||
/// # fn insert_at(index: usize, value: i32, list: &mut Vec<i32>) -> &i32 {
|
||||
/// # list.insert(index, value);
|
||||
/// # &list[index]
|
||||
/// # }
|
||||
/// let func = DynamicFunction::new(
|
||||
/// |mut args| {
|
||||
/// let index = args.take::<usize>()?;
|
||||
/// let value = args.take::<i32>()?;
|
||||
/// let list = args.take::<&mut Vec<i32>>()?;
|
||||
///
|
||||
/// let result = insert_at(index, value, list);
|
||||
///
|
||||
/// Ok(result.into_return())
|
||||
/// },
|
||||
/// SignatureInfo::named("insert_at")
|
||||
/// .with_arg::<usize>("index")
|
||||
/// .with_arg::<i32>("value")
|
||||
/// .with_arg::<&mut Vec<i32>>("list")
|
||||
/// .with_return::<&i32>(),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// However, this is both verbose and error-prone.
|
||||
/// What happens if we forget to add an argument to the [`SignatureInfo`]?
|
||||
/// What if we forget to set the return type?
|
||||
///
|
||||
/// This macro can be used to generate the above code safely, automatically,
|
||||
/// and with less boilerplate:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::ArgList;
|
||||
/// # use bevy_reflect::reflect_fn;
|
||||
/// # fn insert_at(index: usize, value: i32, list: &mut Vec<i32>) -> &i32 {
|
||||
/// # list.insert(index, value);
|
||||
/// # &list[index]
|
||||
/// # }
|
||||
/// let func = reflect_fn!(
|
||||
/// fn insert_at(index: usize, value: i32, list: &mut Vec<i32>) -> &i32 {
|
||||
/// insert_at(index, value, list)
|
||||
/// }
|
||||
/// );
|
||||
/// # // Sanity tests:
|
||||
/// # let info = func.info();
|
||||
/// # assert_eq!(info.name().unwrap(), "insert_at");
|
||||
/// # assert_eq!(info.base().arg_count(), 3);
|
||||
/// # assert_eq!(info.base().args()[0].name(), Some("index"));
|
||||
/// # assert!(info.base().args()[0].is::<usize>());
|
||||
/// # assert_eq!(info.base().args()[1].name(), Some("value"));
|
||||
/// # assert!(info.base().args()[1].is::<i32>());
|
||||
/// # assert_eq!(info.base().args()[2].name(), Some("list"));
|
||||
/// # assert!(info.base().args()[2].is::<&mut Vec<i32>>());
|
||||
/// # assert!(info.base().return_info().is::<&i32>());
|
||||
/// #
|
||||
/// # let mut list = vec![1, 2, 3];
|
||||
/// # let args = ArgList::new().push_owned(0_usize).push_owned(5_i32).push_mut(&mut list);
|
||||
/// # let result = func.call(args).unwrap().unwrap_ref();
|
||||
/// # assert_eq!(result.try_downcast_ref::<i32>(), Some(&5));
|
||||
/// # assert_eq!(list, vec![5, 1, 2, 3]);
|
||||
/// ```
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// The macro expects the following syntax:
|
||||
///
|
||||
/// ```text
|
||||
/// MUT MOVE fn NAME ( ARGS ) RETURN BLOCK
|
||||
/// ```
|
||||
///
|
||||
/// - `MUT`: `mut` | _none_
|
||||
/// - If present, the generated function will instead be a [`DynamicFunctionMut`].
|
||||
/// - `MOVE`: `move` | _none_
|
||||
/// - If present, adds the `move` keyword to the internal closure.
|
||||
/// - `NAME`: Block | Identifier | Literal | _none_
|
||||
/// - If present, defines the name of the function for the [`SignatureInfo`].
|
||||
/// - Blocks should evaluate to a string. Identifiers will be stringified.
|
||||
/// And Literals will be used as-is.
|
||||
/// - `ARGS`: ( `mut`? Identifier `:` Type `,`? )*
|
||||
/// - The list of 0 or more arguments the function accepts.
|
||||
/// - `RETURN`: `->` Type | _none_
|
||||
/// - If present, defines the return type of the function.
|
||||
/// Otherwise, the return type is assumed to be `()`.
|
||||
/// - `BLOCK`: Block
|
||||
/// - The block of code that the function will execute.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Defining anonymous functions:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::reflect_fn;
|
||||
/// let func = reflect_fn!(
|
||||
/// fn(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// let info = func.info();
|
||||
/// assert_eq!(info.name(), None);
|
||||
/// ```
|
||||
///
|
||||
/// Defining functions with computed names:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::reflect_fn;
|
||||
/// let func = reflect_fn!(
|
||||
/// fn {concat!("a", "d", "d")} (a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// let info = func.info();
|
||||
/// assert_eq!(info.name().unwrap(), "add");
|
||||
/// ```
|
||||
///
|
||||
/// Defining functions with literal names:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::reflect_fn;
|
||||
/// let func = reflect_fn!(
|
||||
/// fn "add two numbers" (a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// let info = func.info();
|
||||
/// assert_eq!(info.name().unwrap(), "add two numbers");
|
||||
/// ```
|
||||
///
|
||||
/// Generating a [`DynamicFunctionMut`]:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::ArgList;
|
||||
/// # use bevy_reflect::reflect_fn;
|
||||
/// let mut list = Vec::<i32>::new();
|
||||
/// let func = reflect_fn!(
|
||||
/// mut fn push(value: i32) {
|
||||
/// list.push(value);
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// let args = ArgList::new().push_owned(123);
|
||||
/// func.call_once(args).unwrap();
|
||||
/// assert_eq!(list, vec![123]);
|
||||
/// ```
|
||||
///
|
||||
/// Capturing variables with `move`:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::reflect_fn;
|
||||
/// # use std::sync::Arc;
|
||||
/// let name = Arc::new(String::from("World"));
|
||||
///
|
||||
/// let name_clone = Arc::clone(&name);
|
||||
/// let func = reflect_fn!(
|
||||
/// move fn print() {
|
||||
/// println!("Hello, {}", name_clone);
|
||||
/// }
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(Arc::strong_count(&name), 2);
|
||||
/// drop(func);
|
||||
/// assert_eq!(Arc::strong_count(&name), 1);
|
||||
/// ```
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [`IntoFunctionMut`]: crate::func::IntoFunctionMut
|
||||
/// [`SignatureInfo`]: crate::func::SignatureInfo
|
||||
#[macro_export]
|
||||
macro_rules! reflect_fn {
|
||||
// === Main === //
|
||||
(@main [$($mut_:tt)?] [$($move_:tt)?] fn $($name:block)? ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@func $($mut_)?)(
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
$($move_)? |mut args| {
|
||||
$(let $arg_name = args.take::<$arg_ty>()?;)*
|
||||
let result = $crate::reflect_fn!(@eval $block $(as $ret_ty)?);
|
||||
::core::result::Result::Ok($crate::func::IntoReturn::into_return(result))
|
||||
},
|
||||
$crate::reflect_fn!(@info $($name)?)
|
||||
$(.with_arg::<$arg_ty>(::core::stringify!($arg_name)))*
|
||||
$(.with_return::<$ret_ty>())?
|
||||
)
|
||||
};
|
||||
|
||||
// === Helpers === //
|
||||
(@func mut) => {
|
||||
$crate::func::DynamicFunctionMut::new
|
||||
};
|
||||
(@func) => {
|
||||
$crate::func::DynamicFunction::new
|
||||
};
|
||||
(@info $name:block) => {
|
||||
$crate::func::SignatureInfo::named($name)
|
||||
};
|
||||
(@info) => {
|
||||
$crate::func::SignatureInfo::anonymous()
|
||||
};
|
||||
(@eval $block:block as $ty:ty) => {
|
||||
// We don't actually use `$ty` here since it can lead to `Missing lifetime specifier` errors.
|
||||
$block
|
||||
};
|
||||
(@eval $block:block) => {{
|
||||
// Ensures that `$block` actually evaluates to `()` and isn't relying on type inference,
|
||||
// which would end up not being reflected by `SignatureInfo` properly.
|
||||
let temp: () = $block;
|
||||
temp
|
||||
}};
|
||||
|
||||
// === Anonymous === //
|
||||
(fn ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [] fn ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut fn ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [] fn ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(move fn ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [move] fn ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut move fn ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [move] fn ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
|
||||
// === Block Named === //
|
||||
(fn $name:block ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [] fn $name ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut fn $name:block ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [] fn $name ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(move fn $name:block ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [move] fn $name ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut move fn $name:block ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [move] fn $name ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
|
||||
// === Ident Named === //
|
||||
(fn $name:ident ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [] fn {::core::stringify!($name)} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut fn $name:ident ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [] fn {::core::stringify!($name)} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(move fn $name:ident ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [move] fn {::core::stringify!($name)} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut move fn $name:ident ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [move] fn {::core::stringify!($name)} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
|
||||
// === Literal Named === //
|
||||
(fn $name:literal ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [] fn {$name} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut fn $name:literal ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [] fn {$name} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(move fn $name:literal ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [] [move] fn {$name} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
(mut move fn $name:literal ($($(mut)? $arg_name:ident : $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? $block:block) => {
|
||||
$crate::reflect_fn!(@main [mut] [move] fn {$name} ($($arg_name : $arg_ty),*) $(-> $ret_ty)? $block)
|
||||
};
|
||||
}
|
||||
|
||||
@ -189,7 +189,6 @@ pub mod signature;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate as bevy_reflect;
|
||||
use alloc::borrow::Cow;
|
||||
|
||||
use super::*;
|
||||
@ -198,7 +197,6 @@ mod tests {
|
||||
func::args::{ArgError, ArgList, Ownership},
|
||||
TypePath,
|
||||
};
|
||||
use bevy_reflect_derive::reflect_fn;
|
||||
|
||||
#[test]
|
||||
fn should_error_on_missing_args() {
|
||||
@ -265,78 +263,4 @@ mod tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_function_with_macro() {
|
||||
let func = reflect_fn!(
|
||||
fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
);
|
||||
|
||||
let info = func.info();
|
||||
|
||||
assert_eq!(info.name().unwrap(), "add");
|
||||
assert_eq!(info.base().arg_count(), 2);
|
||||
assert_eq!(info.base().args()[0].name(), Some("a"));
|
||||
assert!(info.base().args()[0].is::<i32>());
|
||||
assert_eq!(info.base().args()[1].name(), Some("b"));
|
||||
assert!(info.base().args()[1].is::<i32>());
|
||||
assert!(info.base().return_info().is::<i32>());
|
||||
|
||||
let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
let result = func.call(args).unwrap().unwrap_owned();
|
||||
assert_eq!(result.try_downcast_ref::<i32>(), Some(&100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_zero_arg_dynamic_function_with_macro() {
|
||||
let func = reflect_fn!(
|
||||
fn shout() {
|
||||
println!("HEY!!!");
|
||||
}
|
||||
);
|
||||
|
||||
let info = func.info();
|
||||
|
||||
assert_eq!(info.name().unwrap(), "shout");
|
||||
assert_eq!(info.base().arg_count(), 0);
|
||||
assert!(info.base().return_info().is::<()>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_anonymous_dynamic_function_with_macro() {
|
||||
let func = reflect_fn!(
|
||||
fn _(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
);
|
||||
|
||||
let info = func.info();
|
||||
assert_eq!(info.name(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_string_named_dynamic_function_with_macro() {
|
||||
let func = reflect_fn!(
|
||||
fn "some cool function"(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
);
|
||||
|
||||
let info = func.info();
|
||||
assert_eq!(info.name().unwrap(), "some cool function");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_expression_named_dynamic_function_with_macro() {
|
||||
let func = reflect_fn!(
|
||||
fn {concat!("some", " cool ", "function")}(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
);
|
||||
|
||||
let info = func.info();
|
||||
assert_eq!(info.name().unwrap(), "some cool function");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user