mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
bevy_reflect: Function reflection terminology refactor (#14813)
# Objective One of the changes in #14704 made `DynamicFunction` effectively the same as `DynamicClosure<'static>`. This change meant that the de facto function type would likely be `DynamicClosure<'static>` instead of the intended `DynamicFunction`, since the former is much more flexible. We _could_ explore ways of making `DynamicFunction` implement `Copy` using some unsafe code, but it likely wouldn't be worth it. And users would likely still reach for the convenience of `DynamicClosure<'static>` over the copy-ability of `DynamicFunction`. The goal of this PR is to fix this confusion between the two types. ## Solution Firstly, the `DynamicFunction` type was removed. Again, it was no different than `DynamicClosure<'static>` so it wasn't a huge deal to remove. Secondly, `DynamicClosure<'env>` and `DynamicClosureMut<'env>` were renamed to `DynamicFunction<'env>` and `DynamicFunctionMut<'env>`, respectively. Yes, we still ultimately kept the naming of `DynamicFunction`, but changed its behavior to that of `DynamicClosure<'env>`. We need a term to refer to both functions and closures, and "function" was the best option. [Originally](https://discord.com/channels/691052431525675048/1002362493634629796/1274091992162242710), I was going to go with "callable" as the replacement term to encompass both functions and closures (e.g. `DynamciCallable<'env>`). However, it was [suggested](https://discord.com/channels/691052431525675048/1002362493634629796/1274653581777047625) by @SkiFire13 that the simpler "function" term could be used instead. While "callable" is perhaps the better umbrella term—being truly ambiguous over functions and closures— "function" is more familiar, used more often, easier to discover, and is subjectively just "better-sounding". ## Testing Most changes are purely swapping type names or updating documentation, but you can verify everything still works by running the following command: ``` cargo test --package bevy_reflect ```
This commit is contained in:
parent
75738ed80f
commit
2b4180ca8f
29 changed files with 775 additions and 1242 deletions
|
@ -1,4 +1,4 @@
|
|||
use bevy_reflect::func::{ArgList, IntoClosure, TypedFunction};
|
||||
use bevy_reflect::func::{ArgList, IntoFunction, TypedFunction};
|
||||
use bevy_reflect::prelude::*;
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
|
||||
|
@ -29,7 +29,7 @@ fn into(c: &mut Criterion) {
|
|||
.bench_function("closure", |b| {
|
||||
let capture = 25;
|
||||
let closure = |a: i32| a + capture;
|
||||
b.iter(|| closure.into_closure());
|
||||
b.iter(|| closure.into_function());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ fn call(c: &mut Criterion) {
|
|||
})
|
||||
.bench_function("closure", |b| {
|
||||
let capture = 25;
|
||||
let add = (|a: i32| a + capture).into_closure();
|
||||
let add = (|a: i32| a + capture).into_function();
|
||||
b.iter_batched(
|
||||
|| ArgList::new().push_owned(75_i32),
|
||||
|args| add.call(args),
|
||||
|
|
|
@ -605,16 +605,16 @@ impl App {
|
|||
|
||||
/// Registers the given function into the [`AppFunctionRegistry`] resource.
|
||||
///
|
||||
/// The given function will internally be stored as a [`DynamicClosure`]
|
||||
/// The given function will internally be stored as a [`DynamicFunction`]
|
||||
/// and mapped according to its [name].
|
||||
///
|
||||
/// Because the function must have a name,
|
||||
/// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead
|
||||
/// be registered using [`register_function_with_name`] or converted to a [`DynamicClosure`]
|
||||
/// and named using [`DynamicClosure::with_name`].
|
||||
/// be registered using [`register_function_with_name`] or converted to a [`DynamicFunction`]
|
||||
/// and named using [`DynamicFunction::with_name`].
|
||||
/// Failure to do so will result in a panic.
|
||||
///
|
||||
/// Only types that implement [`IntoClosure`] may be registered via this method.
|
||||
/// Only types that implement [`IntoFunction`] may be registered via this method.
|
||||
///
|
||||
/// See [`FunctionRegistry::register`] for more information.
|
||||
///
|
||||
|
@ -650,7 +650,7 @@ impl App {
|
|||
/// .register_function(add);
|
||||
/// ```
|
||||
///
|
||||
/// Anonymous functions and closures should be registered using [`register_function_with_name`] or given a name using [`DynamicClosure::with_name`].
|
||||
/// Anonymous functions and closures should be registered using [`register_function_with_name`] or given a name using [`DynamicFunction::with_name`].
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use bevy_app::App;
|
||||
|
@ -660,15 +660,15 @@ impl App {
|
|||
/// ```
|
||||
///
|
||||
/// [`register_function_with_name`]: Self::register_function_with_name
|
||||
/// [`DynamicClosure`]: bevy_reflect::func::DynamicClosure
|
||||
/// [`DynamicFunction`]: bevy_reflect::func::DynamicFunction
|
||||
/// [name]: bevy_reflect::func::FunctionInfo::name
|
||||
/// [`DynamicClosure::with_name`]: bevy_reflect::func::DynamicClosure::with_name
|
||||
/// [`IntoClosure`]: bevy_reflect::func::IntoClosure
|
||||
/// [`DynamicFunction::with_name`]: bevy_reflect::func::DynamicFunction::with_name
|
||||
/// [`IntoFunction`]: bevy_reflect::func::IntoFunction
|
||||
/// [`FunctionRegistry::register`]: bevy_reflect::func::FunctionRegistry::register
|
||||
#[cfg(feature = "reflect_functions")]
|
||||
pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
|
||||
where
|
||||
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
|
||||
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
self.main_mut().register_function(function);
|
||||
self
|
||||
|
@ -689,7 +689,7 @@ impl App {
|
|||
/// For named functions (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`) where a custom name is not needed,
|
||||
/// it's recommended to use [`register_function`] instead as the generated name is guaranteed to be unique.
|
||||
///
|
||||
/// Only types that implement [`IntoClosure`] may be registered via this method.
|
||||
/// Only types that implement [`IntoFunction`] may be registered via this method.
|
||||
///
|
||||
/// See [`FunctionRegistry::register_with_name`] for more information.
|
||||
///
|
||||
|
@ -738,7 +738,7 @@ impl App {
|
|||
///
|
||||
/// [type name]: std::any::type_name
|
||||
/// [`register_function`]: Self::register_function
|
||||
/// [`IntoClosure`]: bevy_reflect::func::IntoClosure
|
||||
/// [`IntoFunction`]: bevy_reflect::func::IntoFunction
|
||||
/// [`FunctionRegistry::register_with_name`]: bevy_reflect::func::FunctionRegistry::register_with_name
|
||||
#[cfg(feature = "reflect_functions")]
|
||||
pub fn register_function_with_name<F, Marker>(
|
||||
|
@ -747,7 +747,7 @@ impl App {
|
|||
function: F,
|
||||
) -> &mut Self
|
||||
where
|
||||
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
|
||||
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
self.main_mut().register_function_with_name(name, function);
|
||||
self
|
||||
|
|
|
@ -413,7 +413,7 @@ impl SubApp {
|
|||
#[cfg(feature = "reflect_functions")]
|
||||
pub fn register_function<F, Marker>(&mut self, function: F) -> &mut Self
|
||||
where
|
||||
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
|
||||
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
let registry = self.world.resource_mut::<AppFunctionRegistry>();
|
||||
registry.write().register(function).unwrap();
|
||||
|
@ -428,7 +428,7 @@ impl SubApp {
|
|||
function: F,
|
||||
) -> &mut Self
|
||||
where
|
||||
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
|
||||
F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
let registry = self.world.resource_mut::<AppFunctionRegistry>();
|
||||
registry.write().register_with_name(name, function).unwrap();
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
#![allow(unused)]
|
||||
|
||||
use bevy_reflect::func::IntoFunction;
|
||||
use bevy_reflect::func::{DynamicFunction, IntoFunction};
|
||||
use bevy_reflect::Reflect;
|
||||
|
||||
fn main() {
|
||||
let value = String::from("Hello, World!");
|
||||
let closure_capture_owned = move || println!("{}", value);
|
||||
|
||||
let _ = closure_capture_owned.into_function();
|
||||
//~^ E0277
|
||||
// Pass:
|
||||
let _: DynamicFunction<'static> = closure_capture_owned.into_function();
|
||||
|
||||
let value = String::from("Hello, World!");
|
||||
let closure_capture_reference = || println!("{}", value);
|
||||
//~^ ERROR: `value` does not live long enough
|
||||
|
||||
let _ = closure_capture_reference.into_function();
|
||||
// ↑ This should be an error (E0277) but `compile_fail_utils` fails to pick it up
|
||||
// when the `closure_capture_owned` test is present
|
||||
let _: DynamicFunction<'static> = closure_capture_reference.into_function();
|
||||
}
|
||||
|
|
|
@ -2,12 +2,10 @@ use crate::func::args::{ArgError, FromArg, Ownership};
|
|||
use crate::{PartialReflect, Reflect, TypePath};
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Represents an argument that can be passed to a [`DynamicFunction`], [`DynamicClosure`],
|
||||
/// or [`DynamicClosureMut`].
|
||||
/// Represents an argument that can be passed to a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::DynamicClosure
|
||||
/// [`DynamicClosureMut`]: crate::func::DynamicClosureMut
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Debug)]
|
||||
pub struct Arg<'a> {
|
||||
index: usize,
|
||||
|
@ -181,9 +179,10 @@ impl<'a> Arg<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents an argument that can be passed to a [`DynamicFunction`].
|
||||
/// Represents an argument that can be passed to a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Debug)]
|
||||
pub enum ArgValue<'a> {
|
||||
Owned(Box<dyn PartialReflect>),
|
||||
|
|
|
@ -3,16 +3,16 @@ use crate::func::args::{Arg, ArgError};
|
|||
/// A trait for types that can be created from an [`Arg`].
|
||||
///
|
||||
/// This trait exists so that types can be automatically converted into an [`Arg`]
|
||||
/// by [`IntoFunction`] so they can be passed to a [`DynamicFunction`] in an [`ArgList`].
|
||||
/// so they can be put into an [`ArgList`] and passed to a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// This trait is used instead of a blanket [`From`] implementation due to coherence issues:
|
||||
/// we can't implement `From<T>` for both `T` and `&T`/`&mut T`.
|
||||
///
|
||||
/// This trait is automatically implemented when using the `Reflect` [derive macro].
|
||||
///
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`ArgList`]: crate::func::args::ArgList
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
/// [derive macro]: derive@crate::Reflect
|
||||
pub trait FromArg {
|
||||
/// The type to convert into.
|
||||
|
|
|
@ -3,13 +3,11 @@ use alloc::borrow::Cow;
|
|||
use crate::func::args::{GetOwnership, Ownership};
|
||||
use crate::TypePath;
|
||||
|
||||
/// Type information for an [`Arg`] used in a [`DynamicFunction`], [`DynamicClosure`],
|
||||
/// or [`DynamicClosureMut`].
|
||||
/// Type information for an [`Arg`] used in a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// [`Arg`]: crate::func::args::Arg
|
||||
/// [`DynamicFunction`]: crate::func::function::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::closures::DynamicClosure
|
||||
/// [`DynamicClosureMut`]: crate::func::closures::DynamicClosureMut
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArgInfo {
|
||||
/// The index of the argument within its function.
|
||||
|
@ -58,13 +56,13 @@ impl ArgInfo {
|
|||
/// since the name can't be inferred from the function type alone.
|
||||
///
|
||||
/// For [`DynamicFunctions`] created using [`IntoFunction`]
|
||||
/// or [`DynamicClosures`] created using [`IntoClosure`],
|
||||
/// and [`DynamicFunctionMuts`] created using [`IntoFunctionMut`],
|
||||
/// the name will always be `None`.
|
||||
///
|
||||
/// [`DynamicFunctions`]: crate::func::DynamicFunction
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [`DynamicClosures`]: crate::func::DynamicClosure
|
||||
/// [`IntoClosure`]: crate::func::IntoClosure
|
||||
/// [`DynamicFunctionMuts`]: crate::func::DynamicFunctionMut
|
||||
/// [`IntoFunctionMut`]: crate::func::IntoFunctionMut
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.name.as_deref()
|
||||
}
|
||||
|
@ -74,6 +72,9 @@ impl ArgInfo {
|
|||
self.ownership
|
||||
}
|
||||
|
||||
/// The [type path] of the argument.
|
||||
///
|
||||
/// [type path]: TypePath::type_path
|
||||
pub fn type_path(&self) -> &'static str {
|
||||
self.type_path
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ use crate::func::ArgError;
|
|||
use crate::{PartialReflect, Reflect, TypePath};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// A list of arguments that can be passed to a [`DynamicFunction`], [`DynamicClosure`],
|
||||
/// or [`DynamicClosureMut`].
|
||||
/// A list of arguments that can be passed to a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -28,8 +27,7 @@ use std::collections::VecDeque;
|
|||
///
|
||||
/// [arguments]: Arg
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::DynamicClosure
|
||||
/// [`DynamicClosureMut`]: crate::func::DynamicClosureMut
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ArgList<'a> {
|
||||
list: VecDeque<Arg<'a>>,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Argument types and utilities for working with [`DynamicFunctions`] and [`DynamicClosures`].
|
||||
//! Argument types and utilities for working with [`DynamicFunction`] and [`DynamicFunctionMut`].
|
||||
//!
|
||||
//! [`DynamicFunctions`]: crate::func::DynamicFunction
|
||||
//! [`DynamicClosures`]: crate::func::DynamicClosure
|
||||
//! [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
//! [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
|
||||
pub use arg::*;
|
||||
pub use error::*;
|
||||
|
|
|
@ -2,12 +2,12 @@ use core::fmt::{Display, Formatter};
|
|||
|
||||
/// A trait for getting the ownership of a type.
|
||||
///
|
||||
/// This trait exists so that [`IntoFunction`] can automatically generate
|
||||
/// This trait exists so that [`TypedFunction`] can automatically generate
|
||||
/// [`FunctionInfo`] containing the proper [`Ownership`] for its [argument] types.
|
||||
///
|
||||
/// This trait is automatically implemented when using the `Reflect` [derive macro].
|
||||
///
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [`TypedFunction`]: crate::func::TypedFunction
|
||||
/// [`FunctionInfo`]: crate::func::FunctionInfo
|
||||
/// [argument]: crate::func::args::Arg
|
||||
/// [derive macro]: derive@crate::Reflect
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
use crate::func::args::{ArgInfo, ArgList};
|
||||
use crate::func::info::FunctionInfo;
|
||||
use crate::func::{
|
||||
DynamicClosureMut, DynamicFunction, FunctionResult, IntoClosure, IntoClosureMut, ReturnInfo,
|
||||
};
|
||||
use alloc::borrow::Cow;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A dynamic representation of a Rust closure.
|
||||
///
|
||||
/// This type can be used to represent any Rust closure that captures its environment immutably.
|
||||
/// For closures that need to capture their environment mutably,
|
||||
/// see [`DynamicClosureMut`].
|
||||
///
|
||||
/// This type can be seen as a superset of [`DynamicFunction`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// You will generally not need to construct this manually.
|
||||
/// Instead, many functions and closures can be automatically converted using the [`IntoClosure`] trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Most of the time, a [`DynamicClosure`] can be created using the [`IntoClosure`] trait:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{ArgList, DynamicClosure, FunctionInfo, IntoClosure};
|
||||
/// #
|
||||
/// let punct = String::from("!!!");
|
||||
///
|
||||
/// let punctuate = |text: &String| -> String {
|
||||
/// format!("{}{}", text, punct)
|
||||
/// };
|
||||
///
|
||||
/// // Convert the closure into a dynamic closure using `IntoClosure::into_closure`
|
||||
/// let mut func: DynamicClosure = punctuate.into_closure();
|
||||
///
|
||||
/// // Dynamically call the closure:
|
||||
/// let text = String::from("Hello, world");
|
||||
/// let args = ArgList::default().push_ref(&text);
|
||||
/// let value = func.call(args).unwrap().unwrap_owned();
|
||||
///
|
||||
/// // Check the result:
|
||||
/// assert_eq!(value.try_take::<String>().unwrap(), "Hello, world!!!");
|
||||
/// ```
|
||||
pub struct DynamicClosure<'env> {
|
||||
pub(super) info: FunctionInfo,
|
||||
pub(super) func: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>,
|
||||
}
|
||||
|
||||
impl<'env> DynamicClosure<'env> {
|
||||
/// Create a new [`DynamicClosure`].
|
||||
///
|
||||
/// The given function can be used to call out to a regular function, closure, or method.
|
||||
///
|
||||
/// It's important that the closure signature matches the provided [`FunctionInfo`].
|
||||
/// This info may be used by consumers of the function for validation and debugging.
|
||||
pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>(
|
||||
func: F,
|
||||
info: FunctionInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
info,
|
||||
func: Arc::new(func),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the name of the closure.
|
||||
///
|
||||
/// For [`DynamicClosures`] created using [`IntoClosure`],
|
||||
/// the default name will always be the full path to the closure as returned by [`std::any::type_name`].
|
||||
///
|
||||
/// This default name generally does not contain the actual name of the closure, only its module path.
|
||||
/// It is therefore recommended to set the name manually using this method.
|
||||
///
|
||||
/// [`DynamicClosures`]: DynamicClosure
|
||||
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
|
||||
self.info = self.info.with_name(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the arguments of the closure.
|
||||
///
|
||||
/// It's important that the arguments match the intended closure signature,
|
||||
/// as this can be used by consumers of the function for validation and debugging.
|
||||
pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self {
|
||||
self.info = self.info.with_args(args);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the return information of the closure.
|
||||
pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self {
|
||||
self.info = self.info.with_return_info(return_info);
|
||||
self
|
||||
}
|
||||
|
||||
/// Call the closure with the given arguments.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{IntoClosure, ArgList};
|
||||
/// let c = 23;
|
||||
/// let add = |a: i32, b: i32| -> i32 {
|
||||
/// a + b + c
|
||||
/// };
|
||||
///
|
||||
/// let mut func = add.into_closure().with_name("add");
|
||||
/// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
/// let result = func.call(args).unwrap().unwrap_owned();
|
||||
/// assert_eq!(result.try_take::<i32>().unwrap(), 123);
|
||||
/// ```
|
||||
pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {
|
||||
(self.func)(args)
|
||||
}
|
||||
|
||||
/// Returns the closure info.
|
||||
pub fn info(&self) -> &FunctionInfo {
|
||||
&self.info
|
||||
}
|
||||
|
||||
/// The [name] of the closure.
|
||||
///
|
||||
/// If this [`DynamicClosure`] was created using [`IntoClosure`],
|
||||
/// then the default name will always be `None`.
|
||||
///
|
||||
/// This can be overridden using [`with_name`].
|
||||
///
|
||||
/// [name]: FunctionInfo::name
|
||||
/// [`with_name`]: Self::with_name
|
||||
pub fn name(&self) -> Option<&Cow<'static, str>> {
|
||||
self.info.name()
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs the closure's signature.
|
||||
///
|
||||
/// This takes the format: `DynamicClosure(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
||||
///
|
||||
/// Names for arguments and the closure itself are optional and will default to `_` if not provided.
|
||||
impl<'env> Debug for DynamicClosure<'env> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let name = self.info.name().unwrap_or(&Cow::Borrowed("_"));
|
||||
write!(f, "DynamicClosure(fn {name}(")?;
|
||||
|
||||
for (index, arg) in self.info.args().iter().enumerate() {
|
||||
let name = arg.name().unwrap_or("_");
|
||||
let ty = arg.type_path();
|
||||
write!(f, "{name}: {ty}")?;
|
||||
|
||||
if index + 1 < self.info.args().len() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
let ret = self.info.return_info().type_path();
|
||||
write!(f, ") -> {ret})")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Clone for DynamicClosure<'env> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
info: self.info.clone(),
|
||||
func: Arc::clone(&self.func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DynamicFunction> for DynamicClosure<'static> {
|
||||
#[inline]
|
||||
fn from(func: DynamicFunction) -> Self {
|
||||
Self {
|
||||
info: func.info,
|
||||
func: func.func,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> IntoClosure<'env, ()> for DynamicClosure<'env> {
|
||||
#[inline]
|
||||
fn into_closure(self) -> DynamicClosure<'env> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> IntoClosureMut<'env, ()> for DynamicClosure<'env> {
|
||||
#[inline]
|
||||
fn into_closure_mut(self) -> DynamicClosureMut<'env> {
|
||||
DynamicClosureMut::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_overwrite_closure_name() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c)
|
||||
.into_closure()
|
||||
.with_name("my_closure");
|
||||
assert_eq!(func.info().name().unwrap(), "my_closure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_convert_dynamic_closure_with_into_closure() {
|
||||
fn make_closure<'env, F: IntoClosure<'env, M>, M>(f: F) -> DynamicClosure<'env> {
|
||||
f.into_closure()
|
||||
}
|
||||
|
||||
let c = 23;
|
||||
let closure: DynamicClosure = make_closure(|a: i32, b: i32| a + b + c);
|
||||
let _: DynamicClosure = make_closure(closure);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_clone_dynamic_closure() {
|
||||
let hello = String::from("Hello");
|
||||
|
||||
let greet = |name: &String| -> String { format!("{}, {}!", hello, name) };
|
||||
|
||||
let greet = greet.into_closure().with_name("greet");
|
||||
let clone = greet.clone();
|
||||
|
||||
assert_eq!(greet.name().unwrap(), "greet");
|
||||
assert_eq!(clone.name().unwrap(), "greet");
|
||||
|
||||
let clone_value = clone
|
||||
.call(ArgList::default().push_ref(&String::from("world")))
|
||||
.unwrap()
|
||||
.unwrap_owned()
|
||||
.try_take::<String>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(clone_value, "Hello, world!");
|
||||
}
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
use alloc::borrow::Cow;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
|
||||
use crate::func::args::{ArgInfo, ArgList};
|
||||
use crate::func::info::FunctionInfo;
|
||||
use crate::func::{DynamicClosure, DynamicFunction, FunctionResult, IntoClosureMut, ReturnInfo};
|
||||
|
||||
/// A dynamic representation of a Rust closure.
|
||||
///
|
||||
/// This type can be used to represent any Rust closure that captures its environment mutably.
|
||||
/// For closures that only need to capture their environment immutably,
|
||||
/// consider using [`DynamicClosure`].
|
||||
///
|
||||
/// This type can be seen as a superset of [`DynamicClosure`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// You will generally not need to construct this manually.
|
||||
/// Instead, many functions and closures can be automatically converted using the [`IntoClosureMut`] trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Most of the time, a [`DynamicClosureMut`] can be created using the [`IntoClosureMut`] trait:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{ArgList, DynamicClosureMut, FunctionInfo, IntoClosureMut};
|
||||
/// #
|
||||
/// let mut list: Vec<i32> = vec![1, 2, 3];
|
||||
///
|
||||
/// // `replace` is a closure that captures a mutable reference to `list`
|
||||
/// let mut replace = |index: usize, value: i32| -> i32 {
|
||||
/// let old_value = list[index];
|
||||
/// list[index] = value;
|
||||
/// old_value
|
||||
/// };
|
||||
///
|
||||
/// // Convert the closure into a dynamic closure using `IntoClosureMut::into_closure_mut`
|
||||
/// let mut func: DynamicClosureMut = replace.into_closure_mut();
|
||||
///
|
||||
/// // Dynamically call the closure:
|
||||
/// let args = ArgList::default().push_owned(1_usize).push_owned(-2_i32);
|
||||
/// let value = func.call(args).unwrap().unwrap_owned();
|
||||
///
|
||||
/// // Check the result:
|
||||
/// assert_eq!(value.try_take::<i32>().unwrap(), 2);
|
||||
///
|
||||
/// // Note that `func` still has a reference to `list`,
|
||||
/// // so we need to drop it before we can access `list` again.
|
||||
/// // Alternatively, we could have called the `func` using
|
||||
/// // `DynamicClosureMut::call_once` to immediately consume the closure.
|
||||
/// drop(func);
|
||||
/// assert_eq!(list, vec![1, -2, 3]);
|
||||
/// ```
|
||||
pub struct DynamicClosureMut<'env> {
|
||||
info: FunctionInfo,
|
||||
func: Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>,
|
||||
}
|
||||
|
||||
impl<'env> DynamicClosureMut<'env> {
|
||||
/// Create a new [`DynamicClosureMut`].
|
||||
///
|
||||
/// The given function can be used to call out to a regular function, closure, or method.
|
||||
///
|
||||
/// It's important that the closure signature matches the provided [`FunctionInfo`].
|
||||
/// This info may be used by consumers of the function for validation and debugging.
|
||||
pub fn new<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>(
|
||||
func: F,
|
||||
info: FunctionInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
info,
|
||||
func: Box::new(func),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the name of the closure.
|
||||
///
|
||||
/// For [`DynamicClosureMuts`] created using [`IntoClosureMut`],
|
||||
/// the default name will always be the full path to the closure as returned by [`std::any::type_name`].
|
||||
///
|
||||
/// This default name generally does not contain the actual name of the closure, only its module path.
|
||||
/// It is therefore recommended to set the name manually using this method.
|
||||
///
|
||||
/// [`DynamicClosureMuts`]: DynamicClosureMut
|
||||
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
|
||||
self.info = self.info.with_name(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the arguments of the closure.
|
||||
///
|
||||
/// It's important that the arguments match the intended closure signature,
|
||||
/// as this can be used by consumers of the function for validation and debugging.
|
||||
pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self {
|
||||
self.info = self.info.with_args(args);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the return information of the closure.
|
||||
pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self {
|
||||
self.info = self.info.with_return_info(return_info);
|
||||
self
|
||||
}
|
||||
|
||||
/// Call the closure with the given arguments.
|
||||
///
|
||||
/// Variables that are captured mutably by this closure
|
||||
/// won't be usable until this closure is dropped.
|
||||
/// Consider using [`call_once`] if you want to consume the closure
|
||||
/// immediately after calling it.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{IntoClosureMut, ArgList};
|
||||
/// let mut total = 0;
|
||||
/// let add = |a: i32, b: i32| -> i32 {
|
||||
/// total = a + b;
|
||||
/// total
|
||||
/// };
|
||||
///
|
||||
/// let mut func = add.into_closure_mut().with_name("add");
|
||||
/// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
/// let result = func.call(args).unwrap().unwrap_owned();
|
||||
/// assert_eq!(result.try_take::<i32>().unwrap(), 100);
|
||||
/// ```
|
||||
///
|
||||
/// [`call_once`]: DynamicClosureMut::call_once
|
||||
pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> {
|
||||
(self.func)(args)
|
||||
}
|
||||
|
||||
/// Call the closure with the given arguments and consume the closure.
|
||||
///
|
||||
/// This is useful for closures that capture their environment mutably
|
||||
/// because otherwise any captured variables would still be borrowed by this closure.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{IntoClosureMut, ArgList};
|
||||
/// let mut count = 0;
|
||||
/// let increment = |amount: i32| count += amount;
|
||||
///
|
||||
/// let increment_function = increment.into_closure_mut();
|
||||
/// let args = ArgList::new().push_owned(5_i32);
|
||||
///
|
||||
/// // We need to drop `increment_function` here so that we
|
||||
/// // can regain access to `count`.
|
||||
/// // `call_once` does this automatically for us.
|
||||
/// increment_function.call_once(args).unwrap();
|
||||
/// assert_eq!(count, 5);
|
||||
/// ```
|
||||
pub fn call_once(mut self, args: ArgList) -> FunctionResult {
|
||||
(self.func)(args)
|
||||
}
|
||||
|
||||
/// Returns the closure info.
|
||||
pub fn info(&self) -> &FunctionInfo {
|
||||
&self.info
|
||||
}
|
||||
|
||||
/// The [name] of the closure.
|
||||
///
|
||||
/// If this [`DynamicClosureMut`] was created using [`IntoClosureMut`],
|
||||
/// then the default name will always be `None`.
|
||||
///
|
||||
/// This can be overridden using [`with_name`].
|
||||
///
|
||||
/// [name]: FunctionInfo::name
|
||||
/// [`with_name`]: Self::with_name
|
||||
pub fn name(&self) -> Option<&Cow<'static, str>> {
|
||||
self.info.name()
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs the closure's signature.
|
||||
///
|
||||
/// This takes the format: `DynamicClosureMut(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
||||
///
|
||||
/// Names for arguments and the closure itself are optional and will default to `_` if not provided.
|
||||
impl<'env> Debug for DynamicClosureMut<'env> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let name = self.info.name().unwrap_or(&Cow::Borrowed("_"));
|
||||
write!(f, "DynamicClosureMut(fn {name}(")?;
|
||||
|
||||
for (index, arg) in self.info.args().iter().enumerate() {
|
||||
let name = arg.name().unwrap_or("_");
|
||||
let ty = arg.type_path();
|
||||
write!(f, "{name}: {ty}")?;
|
||||
|
||||
if index + 1 < self.info.args().len() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
let ret = self.info.return_info().type_path();
|
||||
write!(f, ") -> {ret})")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DynamicFunction> for DynamicClosureMut<'static> {
|
||||
#[inline]
|
||||
fn from(func: DynamicFunction) -> Self {
|
||||
Self {
|
||||
info: func.info,
|
||||
func: Box::new(move |args| (func.func)(args)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> From<DynamicClosure<'env>> for DynamicClosureMut<'env> {
|
||||
#[inline]
|
||||
fn from(closure: DynamicClosure<'env>) -> Self {
|
||||
Self {
|
||||
info: closure.info,
|
||||
func: Box::new(move |args| (closure.func)(args)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> IntoClosureMut<'env, ()> for DynamicClosureMut<'env> {
|
||||
#[inline]
|
||||
fn into_closure_mut(self) -> DynamicClosureMut<'env> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_overwrite_closure_name() {
|
||||
let mut total = 0;
|
||||
let func = (|a: i32, b: i32| total = a + b)
|
||||
.into_closure_mut()
|
||||
.with_name("my_closure");
|
||||
assert_eq!(func.info().name().unwrap(), "my_closure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_convert_dynamic_closure_mut_with_into_closure() {
|
||||
fn make_closure<'env, F: IntoClosureMut<'env, M>, M>(f: F) -> DynamicClosureMut<'env> {
|
||||
f.into_closure_mut()
|
||||
}
|
||||
|
||||
let mut total = 0;
|
||||
let closure: DynamicClosureMut = make_closure(|a: i32, b: i32| total = a + b);
|
||||
let _: DynamicClosureMut = make_closure(closure);
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
use crate::func::{DynamicClosure, ReflectFn, TypedFunction};
|
||||
|
||||
/// A trait for types that can be converted into a [`DynamicClosure`].
|
||||
///
|
||||
/// This trait is automatically implemented for any type that implements
|
||||
/// [`ReflectFn`] and [`TypedFunction`].
|
||||
///
|
||||
/// This trait can be seen as a supertrait of [`IntoFunction`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [module-level documentation]: crate::func
|
||||
pub trait IntoClosure<'env, Marker> {
|
||||
/// Converts [`Self`] into a [`DynamicClosure`].
|
||||
fn into_closure(self) -> DynamicClosure<'env>;
|
||||
}
|
||||
|
||||
impl<'env, F, Marker1, Marker2> IntoClosure<'env, (Marker1, Marker2)> for F
|
||||
where
|
||||
F: ReflectFn<'env, Marker1> + TypedFunction<Marker2> + Send + Sync + 'env,
|
||||
{
|
||||
fn into_closure(self) -> DynamicClosure<'env> {
|
||||
DynamicClosure::new(move |args| self.reflect_call(args), Self::function_info())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::func::ArgList;
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_closure_from_closure() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c).into_closure();
|
||||
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(&123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_closure_from_function() {
|
||||
fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
let func = add.into_closure();
|
||||
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_default_closure_name_to_none() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c).into_closure();
|
||||
assert_eq!(func.info().name(), None);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
use crate::func::{DynamicClosureMut, ReflectFnMut, TypedFunction};
|
||||
|
||||
/// A trait for types that can be converted into a [`DynamicClosureMut`].
|
||||
///
|
||||
/// This trait is automatically implemented for any type that implements
|
||||
/// [`ReflectFnMut`] and [`TypedFunction`].
|
||||
///
|
||||
/// This trait can be seen as a supertrait of [`IntoClosure`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// [`ReflectFn`]: crate::func::ReflectFn
|
||||
/// [`IntoClosure`]: crate::func::closures::IntoClosure
|
||||
/// [module-level documentation]: crate::func
|
||||
pub trait IntoClosureMut<'env, Marker> {
|
||||
/// Converts [`Self`] into a [`DynamicClosureMut`].
|
||||
fn into_closure_mut(self) -> DynamicClosureMut<'env>;
|
||||
}
|
||||
|
||||
impl<'env, F, Marker1, Marker2> IntoClosureMut<'env, (Marker1, Marker2)> for F
|
||||
where
|
||||
F: ReflectFnMut<'env, Marker1> + TypedFunction<Marker2> + 'env,
|
||||
{
|
||||
fn into_closure_mut(mut self) -> DynamicClosureMut<'env> {
|
||||
DynamicClosureMut::new(
|
||||
move |args| self.reflect_call_mut(args),
|
||||
Self::function_info(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::func::{ArgList, IntoClosure};
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_closure_mut_from_closure() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c).into_closure();
|
||||
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(&123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_closure_mut_from_closure_with_mutable_capture() {
|
||||
let mut total = 0;
|
||||
let func = (|a: i32, b: i32| total = a + b).into_closure_mut();
|
||||
let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
func.call_once(args).unwrap();
|
||||
assert_eq!(total, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_closure_mut_from_function() {
|
||||
fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
let mut func = add.into_closure_mut();
|
||||
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_default_closure_name_to_none() {
|
||||
let mut total = 0;
|
||||
let func = (|a: i32, b: i32| total = a + b).into_closure_mut();
|
||||
assert_eq!(func.info().name(), None);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
pub use dynamic_closure::*;
|
||||
pub use dynamic_closure_mut::*;
|
||||
pub use into_closure::*;
|
||||
pub use into_closure_mut::*;
|
||||
|
||||
mod dynamic_closure;
|
||||
mod dynamic_closure_mut;
|
||||
mod into_closure;
|
||||
mod into_closure_mut;
|
232
crates/bevy_reflect/src/func/dynamic_function.rs
Normal file
232
crates/bevy_reflect/src/func/dynamic_function.rs
Normal file
|
@ -0,0 +1,232 @@
|
|||
use crate::func::args::{ArgInfo, ArgList};
|
||||
use crate::func::info::FunctionInfo;
|
||||
use crate::func::{DynamicFunctionMut, FunctionResult, IntoFunction, IntoFunctionMut, ReturnInfo};
|
||||
use alloc::borrow::Cow;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A dynamic representation of a function.
|
||||
///
|
||||
/// This type can be used to represent any callable that satisfies [`Fn`]
|
||||
/// (or the reflection-based equivalent, [`ReflectFn`]).
|
||||
/// That is, any function or closure that does not mutably borrow data from its environment.
|
||||
///
|
||||
/// For functions that do need to capture their environment mutably (i.e. mutable closures),
|
||||
/// see [`DynamicFunctionMut`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// You will generally not need to construct this manually.
|
||||
/// Instead, many functions and closures can be automatically converted using the [`IntoFunction`] trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Most of the time, a [`DynamicFunction`] can be created using the [`IntoFunction`] trait:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{ArgList, DynamicFunction, FunctionInfo, IntoFunction};
|
||||
/// #
|
||||
/// fn add(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
///
|
||||
/// // Convert the function into a dynamic function using `IntoFunction::into_function`:
|
||||
/// let mut func: DynamicFunction = add.into_function();
|
||||
///
|
||||
/// // Dynamically call it:
|
||||
/// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32);
|
||||
/// let value = func.call(args).unwrap().unwrap_owned();
|
||||
///
|
||||
/// // Check the result:
|
||||
/// assert_eq!(value.try_downcast_ref::<i32>(), Some(&100));
|
||||
/// ```
|
||||
///
|
||||
/// [`ReflectFn`]: crate::func::ReflectFn
|
||||
/// [module-level documentation]: crate::func
|
||||
pub struct DynamicFunction<'env> {
|
||||
pub(super) info: FunctionInfo,
|
||||
pub(super) func: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>,
|
||||
}
|
||||
|
||||
impl<'env> DynamicFunction<'env> {
|
||||
/// Create a new [`DynamicFunction`].
|
||||
///
|
||||
/// The given function can be used to call out to any other callable,
|
||||
/// including functions, closures, or methods.
|
||||
///
|
||||
/// It's important that the function signature matches the provided [`FunctionInfo`].
|
||||
/// This info may be used by consumers of this function for validation and debugging.
|
||||
pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>(
|
||||
func: F,
|
||||
info: FunctionInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
info,
|
||||
func: Arc::new(func),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the name of the function.
|
||||
///
|
||||
/// For [`DynamicFunctions`] created using [`IntoFunction`],
|
||||
/// the default name will always be the full path to the function as returned by [`std::any::type_name`],
|
||||
/// unless the function is a closure, anonymous function, or function pointer,
|
||||
/// in which case the name will be `None`.
|
||||
///
|
||||
/// [`DynamicFunctions`]: DynamicFunction
|
||||
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
|
||||
self.info = self.info.with_name(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the argument information of the function.
|
||||
///
|
||||
/// It's important that the arguments match the intended function signature,
|
||||
/// as this can be used by consumers of this function for validation and debugging.
|
||||
pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self {
|
||||
self.info = self.info.with_args(args);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the return information of the function.
|
||||
pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self {
|
||||
self.info = self.info.with_return_info(return_info);
|
||||
self
|
||||
}
|
||||
|
||||
/// Call the function with the given arguments.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{IntoFunction, ArgList};
|
||||
/// let c = 23;
|
||||
/// let add = |a: i32, b: i32| -> i32 {
|
||||
/// a + b + c
|
||||
/// };
|
||||
///
|
||||
/// let mut func = add.into_function().with_name("add");
|
||||
/// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
/// let result = func.call(args).unwrap().unwrap_owned();
|
||||
/// assert_eq!(result.try_take::<i32>().unwrap(), 123);
|
||||
/// ```
|
||||
pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {
|
||||
(self.func)(args)
|
||||
}
|
||||
|
||||
/// Returns the function info.
|
||||
pub fn info(&self) -> &FunctionInfo {
|
||||
&self.info
|
||||
}
|
||||
|
||||
/// The [name] of the function.
|
||||
///
|
||||
/// For [`DynamicFunctions`] created using [`IntoFunction`],
|
||||
/// the default name will always be the full path to the function as returned by [`std::any::type_name`],
|
||||
/// unless the function is a closure, anonymous function, or function pointer,
|
||||
/// in which case the name will be `None`.
|
||||
///
|
||||
/// This can be overridden using [`with_name`].
|
||||
///
|
||||
/// [name]: FunctionInfo::name
|
||||
/// [`DynamicFunctions`]: DynamicFunction
|
||||
/// [`with_name`]: Self::with_name
|
||||
pub fn name(&self) -> Option<&Cow<'static, str>> {
|
||||
self.info.name()
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs the function's signature.
|
||||
///
|
||||
/// This takes the format: `DynamicFunction(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
||||
///
|
||||
/// Names for arguments and the function itself are optional and will default to `_` if not provided.
|
||||
impl<'env> Debug for DynamicFunction<'env> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let name = self.info.name().unwrap_or(&Cow::Borrowed("_"));
|
||||
write!(f, "DynamicFunction(fn {name}(")?;
|
||||
|
||||
for (index, arg) in self.info.args().iter().enumerate() {
|
||||
let name = arg.name().unwrap_or("_");
|
||||
let ty = arg.type_path();
|
||||
write!(f, "{name}: {ty}")?;
|
||||
|
||||
if index + 1 < self.info.args().len() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
let ret = self.info.return_info().type_path();
|
||||
write!(f, ") -> {ret})")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Clone for DynamicFunction<'env> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
info: self.info.clone(),
|
||||
func: Arc::clone(&self.func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> IntoFunction<'env, ()> for DynamicFunction<'env> {
|
||||
#[inline]
|
||||
fn into_function(self) -> DynamicFunction<'env> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> IntoFunctionMut<'env, ()> for DynamicFunction<'env> {
|
||||
#[inline]
|
||||
fn into_function_mut(self) -> DynamicFunctionMut<'env> {
|
||||
DynamicFunctionMut::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_overwrite_function_name() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c)
|
||||
.into_function()
|
||||
.with_name("my_function");
|
||||
assert_eq!(func.info().name().unwrap(), "my_function");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_convert_dynamic_function_with_into_function() {
|
||||
fn make_closure<'env, F: IntoFunction<'env, M>, M>(f: F) -> DynamicFunction<'env> {
|
||||
f.into_function()
|
||||
}
|
||||
|
||||
let c = 23;
|
||||
let function: DynamicFunction = make_closure(|a: i32, b: i32| a + b + c);
|
||||
let _: DynamicFunction = make_closure(function);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_clone_dynamic_function() {
|
||||
let hello = String::from("Hello");
|
||||
|
||||
let greet = |name: &String| -> String { format!("{}, {}!", hello, name) };
|
||||
|
||||
let greet = greet.into_function().with_name("greet");
|
||||
let clone = greet.clone();
|
||||
|
||||
assert_eq!(greet.name().unwrap(), "greet");
|
||||
assert_eq!(clone.name().unwrap(), "greet");
|
||||
|
||||
let clone_value = clone
|
||||
.call(ArgList::default().push_ref(&String::from("world")))
|
||||
.unwrap()
|
||||
.unwrap_owned()
|
||||
.try_take::<String>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(clone_value, "Hello, world!");
|
||||
}
|
||||
}
|
255
crates/bevy_reflect/src/func/dynamic_function_mut.rs
Normal file
255
crates/bevy_reflect/src/func/dynamic_function_mut.rs
Normal file
|
@ -0,0 +1,255 @@
|
|||
use alloc::borrow::Cow;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
|
||||
use crate::func::args::{ArgInfo, ArgList};
|
||||
use crate::func::info::FunctionInfo;
|
||||
use crate::func::{DynamicFunction, FunctionResult, IntoFunctionMut, ReturnInfo};
|
||||
|
||||
/// A dynamic representation of a function.
|
||||
///
|
||||
/// This type can be used to represent any callable that satisfies [`FnMut`]
|
||||
/// (or the reflection-based equivalent, [`ReflectFnMut`]).
|
||||
/// That is, any function or closure.
|
||||
///
|
||||
/// For functions that do not need to capture their environment mutably,
|
||||
/// it's recommended to use [`DynamicFunction`] instead.
|
||||
///
|
||||
/// This type can be seen as a superset of [`DynamicFunction`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// You will generally not need to construct this manually.
|
||||
/// Instead, many functions and closures can be automatically converted using the [`IntoFunctionMut`] trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Most of the time, a [`DynamicFunctionMut`] can be created using the [`IntoFunctionMut`] trait:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{ArgList, DynamicFunctionMut, FunctionInfo, IntoFunctionMut};
|
||||
/// #
|
||||
/// let mut list: Vec<i32> = vec![1, 2, 3];
|
||||
///
|
||||
/// // `replace` is a closure that captures a mutable reference to `list`
|
||||
/// let mut replace = |index: usize, value: i32| -> i32 {
|
||||
/// let old_value = list[index];
|
||||
/// list[index] = value;
|
||||
/// old_value
|
||||
/// };
|
||||
///
|
||||
/// // Since this closure mutably borrows data, we can't convert it into a regular `DynamicFunction`,
|
||||
/// // as doing so would result in a compile-time error:
|
||||
/// // let mut func: DynamicFunction = replace.into_function();
|
||||
///
|
||||
/// // Instead, we convert it into a `DynamicFunctionMut` using `IntoFunctionMut::into_function_mut`:
|
||||
/// let mut func: DynamicFunctionMut = replace.into_function_mut();
|
||||
///
|
||||
/// // Dynamically call it:
|
||||
/// let args = ArgList::default().push_owned(1_usize).push_owned(-2_i32);
|
||||
/// let value = func.call(args).unwrap().unwrap_owned();
|
||||
///
|
||||
/// // Check the result:
|
||||
/// assert_eq!(value.try_take::<i32>().unwrap(), 2);
|
||||
///
|
||||
/// // Note that `func` still has a reference to `list`,
|
||||
/// // so we need to drop it before we can access `list` again.
|
||||
/// // Alternatively, we could have invoked `func` with
|
||||
/// // `DynamicFunctionMut::call_once` to immediately consume it.
|
||||
/// drop(func);
|
||||
/// assert_eq!(list, vec![1, -2, 3]);
|
||||
/// ```
|
||||
///
|
||||
/// [`ReflectFnMut`]: crate::func::ReflectFnMut
|
||||
/// [module-level documentation]: crate::func
|
||||
pub struct DynamicFunctionMut<'env> {
|
||||
info: FunctionInfo,
|
||||
func: Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>,
|
||||
}
|
||||
|
||||
impl<'env> DynamicFunctionMut<'env> {
|
||||
/// Create a new [`DynamicFunctionMut`].
|
||||
///
|
||||
/// The given function can be used to call out to any other callable,
|
||||
/// including functions, closures, or methods.
|
||||
///
|
||||
/// It's important that the function signature matches the provided [`FunctionInfo`].
|
||||
/// This info may be used by consumers of this function for validation and debugging.
|
||||
pub fn new<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>(
|
||||
func: F,
|
||||
info: FunctionInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
info,
|
||||
func: Box::new(func),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the name of the function.
|
||||
///
|
||||
/// For [`DynamicFunctionMuts`] created using [`IntoFunctionMut`],
|
||||
/// the default name will always be the full path to the function as returned by [`std::any::type_name`],
|
||||
/// unless the function is a closure, anonymous function, or function pointer,
|
||||
/// in which case the name will be `None`.
|
||||
///
|
||||
/// [`DynamicFunctionMuts`]: DynamicFunctionMut
|
||||
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
|
||||
self.info = self.info.with_name(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the argument information of the function.
|
||||
///
|
||||
/// It's important that the arguments match the intended function signature,
|
||||
/// as this can be used by consumers of this function for validation and debugging.
|
||||
pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self {
|
||||
self.info = self.info.with_args(args);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the return information of the function.
|
||||
pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self {
|
||||
self.info = self.info.with_return_info(return_info);
|
||||
self
|
||||
}
|
||||
|
||||
/// Call the function with the given arguments.
|
||||
///
|
||||
/// Variables that are captured mutably by this function
|
||||
/// won't be usable until this function is dropped.
|
||||
/// Consider using [`call_once`] if you want to consume the function
|
||||
/// immediately after calling it.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{IntoFunctionMut, ArgList};
|
||||
/// let mut total = 0;
|
||||
/// let add = |a: i32, b: i32| -> i32 {
|
||||
/// total = a + b;
|
||||
/// total
|
||||
/// };
|
||||
///
|
||||
/// let mut func = add.into_function_mut().with_name("add");
|
||||
/// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
/// let result = func.call(args).unwrap().unwrap_owned();
|
||||
/// assert_eq!(result.try_take::<i32>().unwrap(), 100);
|
||||
/// ```
|
||||
///
|
||||
/// [`call_once`]: DynamicFunctionMut::call_once
|
||||
pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> {
|
||||
(self.func)(args)
|
||||
}
|
||||
|
||||
/// Call the function with the given arguments and consume it.
|
||||
///
|
||||
/// This is useful for functions that capture their environment mutably
|
||||
/// because otherwise any captured variables would still be borrowed by it.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{IntoFunctionMut, ArgList};
|
||||
/// let mut count = 0;
|
||||
/// let increment = |amount: i32| count += amount;
|
||||
///
|
||||
/// let increment_function = increment.into_function_mut();
|
||||
/// let args = ArgList::new().push_owned(5_i32);
|
||||
///
|
||||
/// // We need to drop `increment_function` here so that we
|
||||
/// // can regain access to `count`.
|
||||
/// // `call_once` does this automatically for us.
|
||||
/// increment_function.call_once(args).unwrap();
|
||||
/// assert_eq!(count, 5);
|
||||
/// ```
|
||||
pub fn call_once(mut self, args: ArgList) -> FunctionResult {
|
||||
(self.func)(args)
|
||||
}
|
||||
|
||||
/// Returns the function info.
|
||||
pub fn info(&self) -> &FunctionInfo {
|
||||
&self.info
|
||||
}
|
||||
|
||||
/// The [name] of the function.
|
||||
///
|
||||
/// For [`DynamicFunctionMuts`] created using [`IntoFunctionMut`],
|
||||
/// the default name will always be the full path to the function as returned by [`std::any::type_name`],
|
||||
/// unless the function is a closure, anonymous function, or function pointer,
|
||||
/// in which case the name will be `None`.
|
||||
///
|
||||
/// This can be overridden using [`with_name`].
|
||||
///
|
||||
/// [name]: FunctionInfo::name
|
||||
/// [`DynamicFunctionMuts`]: DynamicFunctionMut
|
||||
/// [`with_name`]: Self::with_name
|
||||
pub fn name(&self) -> Option<&Cow<'static, str>> {
|
||||
self.info.name()
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs the function's signature.
|
||||
///
|
||||
/// This takes the format: `DynamicFunctionMut(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
||||
///
|
||||
/// Names for arguments and the function itself are optional and will default to `_` if not provided.
|
||||
impl<'env> Debug for DynamicFunctionMut<'env> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let name = self.info.name().unwrap_or(&Cow::Borrowed("_"));
|
||||
write!(f, "DynamicFunctionMut(fn {name}(")?;
|
||||
|
||||
for (index, arg) in self.info.args().iter().enumerate() {
|
||||
let name = arg.name().unwrap_or("_");
|
||||
let ty = arg.type_path();
|
||||
write!(f, "{name}: {ty}")?;
|
||||
|
||||
if index + 1 < self.info.args().len() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
let ret = self.info.return_info().type_path();
|
||||
write!(f, ") -> {ret})")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> From<DynamicFunction<'env>> for DynamicFunctionMut<'env> {
|
||||
#[inline]
|
||||
fn from(function: DynamicFunction<'env>) -> Self {
|
||||
Self {
|
||||
info: function.info,
|
||||
func: Box::new(move |args| (function.func)(args)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> IntoFunctionMut<'env, ()> for DynamicFunctionMut<'env> {
|
||||
#[inline]
|
||||
fn into_function_mut(self) -> DynamicFunctionMut<'env> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_overwrite_function_name() {
|
||||
let mut total = 0;
|
||||
let func = (|a: i32, b: i32| total = a + b)
|
||||
.into_function_mut()
|
||||
.with_name("my_function");
|
||||
assert_eq!(func.info().name().unwrap(), "my_function");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_convert_dynamic_function_mut_with_into_function() {
|
||||
fn make_closure<'env, F: IntoFunctionMut<'env, M>, M>(f: F) -> DynamicFunctionMut<'env> {
|
||||
f.into_function_mut()
|
||||
}
|
||||
|
||||
let mut total = 0;
|
||||
let closure: DynamicFunctionMut = make_closure(|a: i32, b: i32| total = a + b);
|
||||
let _: DynamicFunctionMut = make_closure(closure);
|
||||
}
|
||||
}
|
|
@ -3,10 +3,10 @@ use crate::func::Return;
|
|||
use alloc::borrow::Cow;
|
||||
use thiserror::Error;
|
||||
|
||||
/// An error that occurs when calling a [`DynamicFunction`] or [`DynamicClosure`].
|
||||
/// An error that occurs when calling a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::DynamicClosure
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum FunctionError {
|
||||
/// An error occurred while converting an argument.
|
||||
|
@ -17,13 +17,13 @@ pub enum FunctionError {
|
|||
ArgCountMismatch { expected: usize, received: usize },
|
||||
}
|
||||
|
||||
/// The result of calling a dynamic [`DynamicFunction`] or [`DynamicClosure`].
|
||||
/// The result of calling a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// Returns `Ok(value)` if the function was called successfully,
|
||||
/// where `value` is the [`Return`] value of the function.
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::DynamicClosure
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
pub type FunctionResult<'a> = Result<Return<'a>, FunctionError>;
|
||||
|
||||
/// An error that occurs when registering a function into a [`FunctionRegistry`].
|
||||
|
|
|
@ -1,292 +0,0 @@
|
|||
use alloc::borrow::Cow;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::func::args::{ArgInfo, ArgList};
|
||||
use crate::func::info::FunctionInfo;
|
||||
use crate::func::{
|
||||
DynamicClosure, DynamicClosureMut, FunctionResult, IntoClosure, IntoClosureMut, IntoFunction,
|
||||
ReturnInfo,
|
||||
};
|
||||
|
||||
/// A dynamic representation of a Rust function.
|
||||
///
|
||||
/// For our purposes, a "function" is just a callable that may not reference its environment.
|
||||
///
|
||||
/// This includes:
|
||||
/// - Functions and methods defined with the `fn` keyword
|
||||
/// - Closures that do not capture their environment
|
||||
/// - Closures that take ownership of captured variables
|
||||
///
|
||||
/// To handle closures that capture references to their environment, see [`DynamicClosure`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// You will generally not need to construct this manually.
|
||||
/// Instead, many functions and closures can be automatically converted using the [`IntoFunction`] trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Most of the time, a [`DynamicFunction`] can be created using the [`IntoFunction`] trait:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::args::ArgList;
|
||||
/// # use bevy_reflect::func::{DynamicFunction, IntoFunction};
|
||||
/// fn add(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
///
|
||||
/// // Convert the function into a dynamic function using `IntoFunction::into_function`
|
||||
/// let mut func: DynamicFunction = add.into_function();
|
||||
///
|
||||
/// // Dynamically call the function:
|
||||
/// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32);
|
||||
/// let value = func.call(args).unwrap().unwrap_owned();
|
||||
///
|
||||
/// // Check the result:
|
||||
/// assert_eq!(value.try_downcast_ref::<i32>(), Some(&100));
|
||||
/// ```
|
||||
///
|
||||
/// However, in some cases, these functions may need to be created manually:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{ArgList, DynamicFunction, FunctionInfo, IntoFunction, Return, ReturnInfo};
|
||||
/// # use bevy_reflect::func::args::ArgInfo;
|
||||
/// fn append(value: String, list: &mut Vec<String>) -> &mut String {
|
||||
/// list.push(value);
|
||||
/// list.last_mut().unwrap()
|
||||
/// }
|
||||
///
|
||||
/// // Due to the return value being a reference that is not tied to the first argument,
|
||||
/// // this will fail to compile:
|
||||
/// // let mut func: DynamicFunction = append.into_function();
|
||||
///
|
||||
/// // Instead, we need to define the function manually.
|
||||
/// // We start by defining the shape of the function:
|
||||
/// let info = FunctionInfo::named("append")
|
||||
/// .with_arg::<String>("value")
|
||||
/// .with_arg::<&mut Vec<String>>("list")
|
||||
/// .with_return::<&mut String>();
|
||||
///
|
||||
/// // Then we define the dynamic function, which will be used to call our `append` function:
|
||||
/// let mut func = DynamicFunction::new(|mut args| {
|
||||
/// // Arguments are removed from the list in order:
|
||||
/// let arg0 = args.take::<String>()?;
|
||||
/// let arg1 = args.take::<&mut Vec<String>>()?;
|
||||
///
|
||||
/// // Then we can call our function and return the result:
|
||||
/// Ok(Return::Mut(append(arg0, arg1)))
|
||||
/// }, info);
|
||||
///
|
||||
/// let mut list = Vec::<String>::new();
|
||||
///
|
||||
/// // Dynamically call the function:
|
||||
/// let args = ArgList::default().push_owned("Hello, World".to_string()).push_mut(&mut list);
|
||||
/// let value = func.call(args).unwrap().unwrap_mut();
|
||||
///
|
||||
/// // Mutate the return value:
|
||||
/// value.try_downcast_mut::<String>().unwrap().push_str("!!!");
|
||||
///
|
||||
/// // Check the result:
|
||||
/// assert_eq!(list, vec!["Hello, World!!!"]);
|
||||
/// ```
|
||||
///
|
||||
/// [module-level documentation]: crate::func
|
||||
pub struct DynamicFunction {
|
||||
pub(super) info: FunctionInfo,
|
||||
pub(super) func: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl DynamicFunction {
|
||||
/// Create a new dynamic [`DynamicFunction`].
|
||||
///
|
||||
/// The given function can be used to call out to a regular function, closure, or method.
|
||||
///
|
||||
/// It's important that the function signature matches the provided [`FunctionInfo`].
|
||||
/// This info may be used by consumers of the function for validation and debugging.
|
||||
pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'static>(
|
||||
func: F,
|
||||
info: FunctionInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
info,
|
||||
func: Arc::new(func),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the name of the function.
|
||||
///
|
||||
/// For [`DynamicFunctions`] created using [`IntoFunction`],
|
||||
/// the default name will always be the full path to the function as returned by [`std::any::type_name`].
|
||||
///
|
||||
/// [`DynamicFunctions`]: DynamicFunction
|
||||
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
|
||||
self.info = self.info.with_name(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the arguments of the function.
|
||||
///
|
||||
/// It's important that the arguments match the intended function signature,
|
||||
/// as this can be used by consumers of the function for validation and debugging.
|
||||
pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self {
|
||||
self.info = self.info.with_args(args);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the return information of the function.
|
||||
pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self {
|
||||
self.info = self.info.with_return_info(return_info);
|
||||
self
|
||||
}
|
||||
|
||||
/// Call the function with the given arguments.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{IntoFunction, ArgList};
|
||||
/// fn add(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
///
|
||||
/// let func = add.into_function();
|
||||
/// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
/// let result = func.call(args).unwrap().unwrap_owned();
|
||||
/// assert_eq!(result.try_take::<i32>().unwrap(), 100);
|
||||
/// ```
|
||||
pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {
|
||||
(self.func)(args)
|
||||
}
|
||||
|
||||
/// Returns the function info.
|
||||
pub fn info(&self) -> &FunctionInfo {
|
||||
&self.info
|
||||
}
|
||||
|
||||
/// The [name] of the function.
|
||||
///
|
||||
/// If this [`DynamicFunction`] was created using [`IntoFunction`],
|
||||
/// then the name will default to one of the following:
|
||||
/// - If the function was anonymous (e.g. `|a: i32, b: i32| { a + b }`),
|
||||
/// then the name will be `None`
|
||||
/// - If the function was named (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`),
|
||||
/// then the name will be the full path to the function as returned by [`std::any::type_name`].
|
||||
///
|
||||
/// This can be overridden using [`with_name`].
|
||||
///
|
||||
/// [name]: FunctionInfo::name
|
||||
/// [`with_name`]: Self::with_name
|
||||
pub fn name(&self) -> Option<&Cow<'static, str>> {
|
||||
self.info.name()
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs the function signature.
|
||||
///
|
||||
/// This takes the format: `DynamicFunction(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
||||
///
|
||||
/// Names for arguments are optional and will default to `_` if not provided.
|
||||
impl Debug for DynamicFunction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let name = self.info.name().unwrap_or(&Cow::Borrowed("_"));
|
||||
write!(f, "DynamicFunction(fn {name}(")?;
|
||||
|
||||
for (index, arg) in self.info.args().iter().enumerate() {
|
||||
let name = arg.name().unwrap_or("_");
|
||||
let ty = arg.type_path();
|
||||
write!(f, "{name}: {ty}")?;
|
||||
|
||||
if index + 1 < self.info.args().len() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
let ret = self.info.return_info().type_path();
|
||||
write!(f, ") -> {ret})")
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for DynamicFunction {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
info: self.info.clone(),
|
||||
func: Arc::clone(&self.func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoFunction<()> for DynamicFunction {
|
||||
#[inline]
|
||||
fn into_function(self) -> DynamicFunction {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoClosure<'_, ()> for DynamicFunction {
|
||||
#[inline]
|
||||
fn into_closure(self) -> DynamicClosure<'static> {
|
||||
DynamicClosure::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoClosureMut<'_, ()> for DynamicFunction {
|
||||
#[inline]
|
||||
fn into_closure_mut(self) -> DynamicClosureMut<'static> {
|
||||
DynamicClosureMut::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::func::Return;
|
||||
|
||||
#[test]
|
||||
fn should_overwrite_function_name() {
|
||||
fn foo() {}
|
||||
|
||||
let func = foo.into_function().with_name("my_function");
|
||||
assert_eq!(func.info().name().unwrap(), "my_function");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_convert_dynamic_function_with_into_function() {
|
||||
fn make_function<F: IntoFunction<M>, M>(f: F) -> DynamicFunction {
|
||||
f.into_function()
|
||||
}
|
||||
|
||||
let function: DynamicFunction = make_function(|| {});
|
||||
let _: DynamicFunction = make_function(function);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_manual_function_construction() {
|
||||
#[allow(clippy::ptr_arg)]
|
||||
fn get(index: usize, list: &Vec<String>) -> &String {
|
||||
&list[index]
|
||||
}
|
||||
|
||||
let func = DynamicFunction::new(
|
||||
|mut args| {
|
||||
let list = args.pop::<&Vec<String>>()?;
|
||||
let index = args.pop::<usize>()?;
|
||||
Ok(Return::Ref(get(index, list)))
|
||||
},
|
||||
FunctionInfo::anonymous()
|
||||
.with_name("get")
|
||||
.with_arg::<usize>("index")
|
||||
.with_arg::<&Vec<String>>("list")
|
||||
.with_return::<&String>(),
|
||||
);
|
||||
|
||||
let list = vec![String::from("foo")];
|
||||
let value = func
|
||||
.call(ArgList::new().push_owned(0_usize).push_ref(&list))
|
||||
.unwrap()
|
||||
.unwrap_ref()
|
||||
.try_downcast_ref::<String>()
|
||||
.unwrap();
|
||||
assert_eq!(value, "foo");
|
||||
}
|
||||
}
|
|
@ -5,13 +5,13 @@ use bevy_utils::all_tuples;
|
|||
use crate::func::args::{ArgInfo, GetOwnership, Ownership};
|
||||
use crate::TypePath;
|
||||
|
||||
/// Type information for a [`DynamicFunction`] or [`DynamicClosure`].
|
||||
/// Type information for a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// This information can be retrieved from certain functions and closures
|
||||
/// using the [`TypedFunction`] trait.
|
||||
/// This information can be retrieved directly from certain functions and closures
|
||||
/// using the [`TypedFunction`] trait, and manually constructed otherwise.
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::DynamicClosure
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionInfo {
|
||||
name: Option<Cow<'static, str>>,
|
||||
|
@ -43,7 +43,7 @@ impl FunctionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a new [`FunctionInfo`] from the given function or closure.
|
||||
/// Create a new [`FunctionInfo`] from the given function.
|
||||
pub fn from<F, Marker>(function: &F) -> Self
|
||||
where
|
||||
F: TypedFunction<Marker>,
|
||||
|
@ -105,13 +105,15 @@ impl FunctionInfo {
|
|||
|
||||
/// The name of the function.
|
||||
///
|
||||
/// For [`DynamicFunctions`] created using [`IntoFunction`] or [`DynamicClosures`] created using [`IntoClosure`],
|
||||
/// the name will always be the full path to the function as returned by [`std::any::type_name`].
|
||||
/// For [`DynamicFunctions`] created using [`IntoFunction`] or [`DynamicFunctionMuts`] created using [`IntoFunctionMut`],
|
||||
/// the default name will always be the full path to the function as returned by [`std::any::type_name`],
|
||||
/// unless the function is a closure, anonymous function, or function pointer,
|
||||
/// in which case the name will be `None`.
|
||||
///
|
||||
/// [`DynamicFunctions`]: crate::func::DynamicFunction
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [`DynamicClosures`]: crate::func::DynamicClosure
|
||||
/// [`IntoClosure`]: crate::func::IntoClosure
|
||||
/// [`DynamicFunctionMuts`]: crate::func::DynamicFunctionMut
|
||||
/// [`IntoFunctionMut`]: crate::func::IntoFunctionMut
|
||||
pub fn name(&self) -> Option<&Cow<'static, str>> {
|
||||
self.name.as_ref()
|
||||
}
|
||||
|
@ -132,10 +134,10 @@ impl FunctionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// Information about the return type of a [`DynamicFunction`] or [`DynamicClosure`].
|
||||
/// Information about the return type of a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::DynamicClosure
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReturnInfo {
|
||||
type_path: &'static str,
|
||||
|
@ -164,13 +166,14 @@ impl ReturnInfo {
|
|||
|
||||
/// A static accessor to compile-time type information for functions.
|
||||
///
|
||||
/// This is the equivalent of [`Typed`] for functions.
|
||||
/// This is the equivalent of [`Typed`], but for function.
|
||||
///
|
||||
/// # Blanket Implementation
|
||||
///
|
||||
/// This trait has a blanket implementation that covers:
|
||||
/// - Functions and methods defined with the `fn` keyword
|
||||
/// - Closures that do not capture their environment
|
||||
/// - Anonymous functions
|
||||
/// - Function pointers
|
||||
/// - Closures that capture immutable references to their environment
|
||||
/// - Closures that capture mutable references to their environment
|
||||
/// - Closures that take ownership of captured variables
|
||||
|
@ -209,6 +212,7 @@ impl ReturnInfo {
|
|||
///
|
||||
/// [module-level documentation]: crate::func
|
||||
/// [`Typed`]: crate::Typed
|
||||
/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html
|
||||
pub trait TypedFunction<Marker> {
|
||||
/// Get the [`FunctionInfo`] for this type.
|
||||
fn function_info() -> FunctionInfo;
|
||||
|
@ -219,7 +223,7 @@ pub trait TypedFunction<Marker> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Helper macro for implementing [`TypedFunction`] on Rust closures.
|
||||
/// Helper macro for implementing [`TypedFunction`] on Rust functions.
|
||||
///
|
||||
/// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`):
|
||||
/// - `FnMut(arg0, arg1, ..., argN) -> R`
|
||||
|
|
|
@ -1,52 +1,34 @@
|
|||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use crate::func::function::DynamicFunction;
|
||||
use crate::func::{ReflectFn, TypedFunction};
|
||||
use crate::func::{DynamicFunction, ReflectFn, TypedFunction};
|
||||
|
||||
/// A trait for types that can be converted into a [`DynamicFunction`].
|
||||
///
|
||||
/// This trait is automatically implemented for many standard Rust functions
|
||||
/// that also implement [`ReflectFn`] and [`TypedFunction`].
|
||||
///
|
||||
/// To handle types such as closures that capture references to their environment,
|
||||
/// see [`IntoClosure`] instead.
|
||||
/// This trait is automatically implemented for any type that implements
|
||||
/// [`ReflectFn`] and [`TypedFunction`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// [`IntoClosure`]: crate::func::IntoClosure
|
||||
/// # Trait Parameters
|
||||
///
|
||||
/// This trait has a `Marker` type parameter that is used to get around issues with
|
||||
/// [unconstrained type parameters] when defining impls with generic arguments or return types.
|
||||
/// This `Marker` can be any type, provided it doesn't conflict with other implementations.
|
||||
///
|
||||
/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function.
|
||||
/// For named functions and some closures, this will end up just being `'static`,
|
||||
/// however, closures that borrow from their environment will have a lifetime bound to that environment.
|
||||
///
|
||||
/// [module-level documentation]: crate::func
|
||||
pub trait IntoFunction<Marker> {
|
||||
/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html
|
||||
pub trait IntoFunction<'env, Marker> {
|
||||
/// Converts [`Self`] into a [`DynamicFunction`].
|
||||
fn into_function(self) -> DynamicFunction;
|
||||
fn into_function(self) -> DynamicFunction<'env>;
|
||||
}
|
||||
|
||||
impl<F, Marker1, Marker2> IntoFunction<(Marker1, Marker2)> for F
|
||||
impl<'env, F, Marker1, Marker2> IntoFunction<'env, (Marker1, Marker2)> for F
|
||||
where
|
||||
F: ReflectFn<'static, Marker1>
|
||||
+ TypedFunction<Marker2>
|
||||
// Ideally, we'd only implement `IntoFunction` on actual function types
|
||||
// (i.e. functions that do not capture their environment at all),
|
||||
// but this would only work if users first explicitly coerced their functions
|
||||
// to a function pointer like `(add as fn(i32, i32) -> i32).into_function()`,
|
||||
// which is certainly not the best user experience.
|
||||
// So as a compromise, we'll stick to allowing any type that implements
|
||||
// `ReflectFn` and `TypedFunction`, but also add the following trait bounds
|
||||
// that all `fn` types implement:
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Unpin
|
||||
+ UnwindSafe
|
||||
+ RefUnwindSafe
|
||||
+ 'static,
|
||||
F: ReflectFn<'env, Marker1> + TypedFunction<Marker2> + Send + Sync + 'env,
|
||||
{
|
||||
fn into_function(self) -> DynamicFunction {
|
||||
// Note that to further guarantee that `self` is a true `fn` type,
|
||||
// we could add a compile time assertion that `F` is zero-sized.
|
||||
// However, we don't do this because it would prevent users from
|
||||
// converting function pointers into `DynamicFunction`s.
|
||||
|
||||
fn into_function(self) -> DynamicFunction<'env> {
|
||||
DynamicFunction::new(move |args| self.reflect_call(args), Self::function_info())
|
||||
}
|
||||
}
|
||||
|
@ -54,9 +36,16 @@ where
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate as bevy_reflect;
|
||||
use crate::func::ArgList;
|
||||
use bevy_reflect_derive::Reflect;
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_function_from_closure() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c).into_function();
|
||||
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(&123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_function_from_function() {
|
||||
|
@ -71,110 +60,9 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_function_from_function_pointer() {
|
||||
fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
let func = (add as fn(i32, i32) -> i32).into_function();
|
||||
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_dynamic_function_from_anonymous_function() {
|
||||
let func = (|a: i32, b: i32| a + b).into_function();
|
||||
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_dynamic_function_from_method() {
|
||||
#[derive(Reflect, Debug, PartialEq)]
|
||||
struct Foo(i32);
|
||||
|
||||
impl Foo {
|
||||
pub fn add(&self, other: &Foo) -> Foo {
|
||||
Foo(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
let foo_a = Foo(25);
|
||||
let foo_b = Foo(75);
|
||||
|
||||
let func = Foo::add.into_function();
|
||||
let args = ArgList::new().push_ref(&foo_a).push_ref(&foo_b);
|
||||
let result = func.call(args).unwrap().unwrap_owned();
|
||||
assert_eq!(result.try_downcast_ref::<Foo>(), Some(&Foo(100)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_zero_args() {
|
||||
fn foo() -> String {
|
||||
String::from("Hello, World!")
|
||||
}
|
||||
|
||||
let func = foo.into_function();
|
||||
let args = ArgList::new();
|
||||
let result = func.call(args).unwrap().unwrap_owned();
|
||||
assert_eq!(
|
||||
result.try_downcast_ref::<String>(),
|
||||
Some(&String::from("Hello, World!"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_unit_return() {
|
||||
fn foo(_: i32) {}
|
||||
|
||||
let func = foo.into_function();
|
||||
let args = ArgList::new().push_owned(123_i32);
|
||||
let result = func.call(args).unwrap();
|
||||
assert!(result.is_unit());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_reference_return() {
|
||||
fn foo<'a>(value: &'a i32, _: String, _: &bool) -> &'a i32 {
|
||||
value
|
||||
}
|
||||
|
||||
let value: i32 = 123;
|
||||
let func = foo.into_function();
|
||||
let args = ArgList::new()
|
||||
.push_ref(&value)
|
||||
.push_owned(String::from("Hello, World!"))
|
||||
.push_ref(&true);
|
||||
let result = func.call(args).unwrap().unwrap_ref();
|
||||
assert_eq!(result.try_downcast_ref::<i32>(), Some(&123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_mutable_reference_return() {
|
||||
fn foo<'a>(value: &'a mut i32, _: String, _: &bool) -> &'a mut i32 {
|
||||
value
|
||||
}
|
||||
|
||||
let mut value: i32 = 123;
|
||||
let func = foo.into_function();
|
||||
let args = ArgList::new()
|
||||
.push_mut(&mut value)
|
||||
.push_owned(String::from("Hello, World!"))
|
||||
.push_ref(&true);
|
||||
let result = func.call(args).unwrap().unwrap_mut();
|
||||
assert_eq!(result.try_downcast_mut::<i32>(), Some(&mut 123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_default_with_function_type_name() {
|
||||
fn foo() {}
|
||||
|
||||
let func = foo.into_function();
|
||||
assert_eq!(
|
||||
func.info().name().unwrap(),
|
||||
"bevy_reflect::func::into_function::tests::should_default_with_function_type_name::foo"
|
||||
);
|
||||
fn should_default_closure_name_to_none() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c).into_function();
|
||||
assert_eq!(func.info().name(), None);
|
||||
}
|
||||
}
|
||||
|
|
83
crates/bevy_reflect/src/func/into_function_mut.rs
Normal file
83
crates/bevy_reflect/src/func/into_function_mut.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use crate::func::{DynamicFunctionMut, ReflectFnMut, TypedFunction};
|
||||
|
||||
/// A trait for types that can be converted into a [`DynamicFunctionMut`].
|
||||
///
|
||||
/// This trait is automatically implemented for any type that implements
|
||||
/// [`ReflectFnMut`] and [`TypedFunction`].
|
||||
///
|
||||
/// This trait can be seen as a superset of [`IntoFunction`].
|
||||
///
|
||||
/// See the [module-level documentation] for more information.
|
||||
///
|
||||
/// # Trait Parameters
|
||||
///
|
||||
/// This trait has a `Marker` type parameter that is used to get around issues with
|
||||
/// [unconstrained type parameters] when defining impls with generic arguments or return types.
|
||||
/// This `Marker` can be any type, provided it doesn't conflict with other implementations.
|
||||
///
|
||||
/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function.
|
||||
/// For named functions and some closures, this will end up just being `'static`,
|
||||
/// however, closures that borrow from their environment will have a lifetime bound to that environment.
|
||||
///
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [module-level documentation]: crate::func
|
||||
/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html
|
||||
pub trait IntoFunctionMut<'env, Marker> {
|
||||
/// Converts [`Self`] into a [`DynamicFunctionMut`].
|
||||
fn into_function_mut(self) -> DynamicFunctionMut<'env>;
|
||||
}
|
||||
|
||||
impl<'env, F, Marker1, Marker2> IntoFunctionMut<'env, (Marker1, Marker2)> for F
|
||||
where
|
||||
F: ReflectFnMut<'env, Marker1> + TypedFunction<Marker2> + 'env,
|
||||
{
|
||||
fn into_function_mut(mut self) -> DynamicFunctionMut<'env> {
|
||||
DynamicFunctionMut::new(
|
||||
move |args| self.reflect_call_mut(args),
|
||||
Self::function_info(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::func::{ArgList, IntoFunction};
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_function_mut_from_closure() {
|
||||
let c = 23;
|
||||
let func = (|a: i32, b: i32| a + b + c).into_function();
|
||||
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(&123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_function_mut_from_closure_with_mutable_capture() {
|
||||
let mut total = 0;
|
||||
let func = (|a: i32, b: i32| total = a + b).into_function_mut();
|
||||
let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
|
||||
func.call_once(args).unwrap();
|
||||
assert_eq!(total, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_dynamic_function_mut_from_function() {
|
||||
fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
let mut func = add.into_function_mut();
|
||||
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_default_closure_name_to_none() {
|
||||
let mut total = 0;
|
||||
let func = (|a: i32, b: i32| total = a + b).into_function_mut();
|
||||
assert_eq!(func.info().name(), None);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
//! Reflection-based dynamic functions.
|
||||
//!
|
||||
//! This module provides a way to pass around and call functions dynamically
|
||||
//! using the [`DynamicFunction`], [`DynamicClosure`], and [`DynamicClosureMut`] types.
|
||||
//! using the [`DynamicFunction`] and [`DynamicFunctionMut`] types.
|
||||
//!
|
||||
//! Many simple functions and closures can be automatically converted to these types
|
||||
//! using the [`IntoFunction`], [`IntoClosure`], and [`IntoClosureMut`] traits, respectively.
|
||||
//! using the [`IntoFunction`] and [`IntoFunctionMut`] traits, respectively.
|
||||
//!
|
||||
//! Once this dynamic representation is created, it can be called with a set of arguments provided
|
||||
//! via an [`ArgList`].
|
||||
|
@ -33,22 +33,28 @@
|
|||
//! assert_eq!(value.unwrap_owned().try_downcast_ref::<i32>(), Some(&100));
|
||||
//! ```
|
||||
//!
|
||||
//! # Functions vs Closures
|
||||
//! # Types of Functions
|
||||
//!
|
||||
//! In Rust, a "function" is any callable that does not capture its environment.
|
||||
//! These are typically defined with the `fn` keyword, but may also use anonymous function syntax.
|
||||
//! For simplicity, this module uses the umbrella term "function" to refer to any Rust callable:
|
||||
//! code that can be invoked with a set of arguments to perform some action.
|
||||
//!
|
||||
//! In Rust, there are two main categories of callables: functions and closures.
|
||||
//!
|
||||
//! A "function" is a callable that does not capture its environment.
|
||||
//! These are typically defined with the `fn` keyword, which are referred to as _named_ functions.
|
||||
//! But they are also _anonymous_ functions, which are unnamed and defined with anonymous function syntax.
|
||||
//!
|
||||
//! ```rust
|
||||
//! // This is a standard Rust function:
|
||||
//! // This is a named function:
|
||||
//! fn add(a: i32, b: i32) -> i32 {
|
||||
//! a + b
|
||||
//! }
|
||||
//!
|
||||
//! // This is an anonymous Rust function:
|
||||
//! // This is an anonymous function:
|
||||
//! let add = |a: i32, b: i32| a + b;
|
||||
//! ```
|
||||
//!
|
||||
//! Rust also has the concept of "closures", which are special functions that capture their environment.
|
||||
//! Closures, on the other hand, are special functions that do capture their environment.
|
||||
//! These are always defined with anonymous function syntax.
|
||||
//!
|
||||
//! ```rust
|
||||
|
@ -65,13 +71,6 @@
|
|||
//! let add = move |a: i32, b: i32| a + b + c;
|
||||
//! ```
|
||||
//!
|
||||
//! Each callable may be considered a subset of the other:
|
||||
//! functions are a subset of immutable closures which are a subset of mutable closures.
|
||||
//!
|
||||
//! This means that, in terms of traits, you could imagine that any type that implements
|
||||
//! [`IntoFunction`], also implements [`IntoClosure`] and [`IntoClosureMut`].
|
||||
//! And every type that implements [`IntoClosure`] also implements [`IntoClosureMut`].
|
||||
//!
|
||||
//! # Valid Signatures
|
||||
//!
|
||||
//! Many of the traits in this module have default blanket implementations over a specific set of function signatures.
|
||||
|
@ -93,7 +92,7 @@
|
|||
//! namely the [lack of variadic generics] and certain [coherence issues].
|
||||
//!
|
||||
//! For other functions that don't conform to one of the above signatures,
|
||||
//! [`DynamicFunction`] and [`DynamicClosure`] can instead be created manually.
|
||||
//! [`DynamicFunction`] and [`DynamicFunctionMut`] can instead be created manually.
|
||||
//!
|
||||
//! [`PartialReflect`]: crate::PartialReflect
|
||||
//! [`Reflect`]: crate::Reflect
|
||||
|
@ -101,22 +100,24 @@
|
|||
//! [coherence issues]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#coherence-leak-check
|
||||
|
||||
pub use args::{ArgError, ArgList, ArgValue};
|
||||
pub use closures::*;
|
||||
pub use dynamic_function::*;
|
||||
pub use dynamic_function_mut::*;
|
||||
pub use error::*;
|
||||
pub use function::*;
|
||||
pub use info::*;
|
||||
pub use into_function::*;
|
||||
pub use into_function_mut::*;
|
||||
pub use reflect_fn::*;
|
||||
pub use reflect_fn_mut::*;
|
||||
pub use registry::*;
|
||||
pub use return_type::*;
|
||||
|
||||
pub mod args;
|
||||
mod closures;
|
||||
mod dynamic_function;
|
||||
mod dynamic_function_mut;
|
||||
mod error;
|
||||
mod function;
|
||||
mod info;
|
||||
mod into_function;
|
||||
mod into_function_mut;
|
||||
pub(crate) mod macros;
|
||||
mod reflect_fn;
|
||||
mod reflect_fn_mut;
|
||||
|
|
|
@ -13,7 +13,8 @@ use crate::{Reflect, TypePath};
|
|||
///
|
||||
/// This trait has a blanket implementation that covers:
|
||||
/// - Functions and methods defined with the `fn` keyword
|
||||
/// - Closures that do not capture their environment
|
||||
/// - Anonymous functions
|
||||
/// - Function pointers
|
||||
/// - Closures that capture immutable references to their environment
|
||||
/// - Closures that take ownership of captured variables
|
||||
///
|
||||
|
@ -24,7 +25,7 @@ use crate::{Reflect, TypePath};
|
|||
///
|
||||
/// See the [module-level documentation] for more information on valid signatures.
|
||||
///
|
||||
/// To handle closures that capture mutable references to their environment,
|
||||
/// To handle functions that capture mutable references to their environment,
|
||||
/// see the [`ReflectFnMut`] trait instead.
|
||||
///
|
||||
/// Arguments are expected to implement [`FromArg`], and the return type is expected to implement [`IntoReturn`].
|
||||
|
@ -52,7 +53,7 @@ use crate::{Reflect, TypePath};
|
|||
/// This `Marker` can be any type, provided it doesn't conflict with other implementations.
|
||||
///
|
||||
/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function.
|
||||
/// For most functions, this will end up just being `'static`,
|
||||
/// For named functions and some closures, this will end up just being `'static`,
|
||||
/// however, closures that borrow from their environment will have a lifetime bound to that environment.
|
||||
///
|
||||
/// [reflection]: crate
|
||||
|
@ -64,7 +65,7 @@ pub trait ReflectFn<'env, Marker>: ReflectFnMut<'env, Marker> {
|
|||
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>;
|
||||
}
|
||||
|
||||
/// Helper macro for implementing [`ReflectFn`] on Rust closures.
|
||||
/// Helper macro for implementing [`ReflectFn`] on Rust functions.
|
||||
///
|
||||
/// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`):
|
||||
/// - `Fn(arg0, arg1, ..., argN) -> R`
|
||||
|
|
|
@ -9,13 +9,15 @@ use crate::{Reflect, TypePath};
|
|||
///
|
||||
/// This allows functions to be called dynamically through [reflection].
|
||||
///
|
||||
/// This is a supertrait of [`ReflectFn`], and is used for closures that may mutate their environment.
|
||||
/// This is a supertrait of [`ReflectFn`], and is used for functions that may mutate their environment,
|
||||
/// such as closures that capture mutable references.
|
||||
///
|
||||
/// # Blanket Implementation
|
||||
///
|
||||
/// This trait has a blanket implementation that covers everything that [`ReflectFn`] does:
|
||||
/// - Functions and methods defined with the `fn` keyword
|
||||
/// - Closures that do not capture their environment
|
||||
/// - Anonymous functions
|
||||
/// - Function pointers
|
||||
/// - Closures that capture immutable references to their environment
|
||||
/// - Closures that take ownership of captured variables
|
||||
///
|
||||
|
@ -57,7 +59,7 @@ use crate::{Reflect, TypePath};
|
|||
/// This `Marker` can be any type, provided it doesn't conflict with other implementations.
|
||||
///
|
||||
/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function.
|
||||
/// For most functions, this will end up just being `'static`,
|
||||
/// For named functions and some closures, this will end up just being `'static`,
|
||||
/// however, closures that borrow from their environment will have a lifetime bound to that environment.
|
||||
///
|
||||
/// [reflection]: crate
|
||||
|
@ -70,7 +72,7 @@ pub trait ReflectFnMut<'env, Marker> {
|
|||
fn reflect_call_mut<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a>;
|
||||
}
|
||||
|
||||
/// Helper macro for implementing [`ReflectFnMut`] on Rust closures.
|
||||
/// Helper macro for implementing [`ReflectFnMut`] on Rust functions.
|
||||
///
|
||||
/// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`):
|
||||
/// - `FnMut(arg0, arg1, ..., argN) -> R`
|
||||
|
|
|
@ -4,40 +4,37 @@ use std::sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
|||
|
||||
use bevy_utils::HashMap;
|
||||
|
||||
use crate::func::{DynamicClosure, FunctionRegistrationError, IntoClosure};
|
||||
use crate::func::{DynamicFunction, FunctionRegistrationError, IntoFunction};
|
||||
|
||||
/// A registry of [reflected functions].
|
||||
///
|
||||
/// This is the function-equivalent to the [`TypeRegistry`].
|
||||
///
|
||||
/// All functions and closures are stored as `'static` closures via [`DynamicClosure<'static>`].
|
||||
/// [`DynamicClosure`] is used instead of [`DynamicFunction`] as it can represent both functions and closures,
|
||||
/// allowing registered functions to contain captured environment data.
|
||||
/// All functions must be `'static` as they are stored as [`DynamicFunction<'static>`].
|
||||
///
|
||||
/// [reflected functions]: crate::func
|
||||
/// [`TypeRegistry`]: crate::TypeRegistry
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
#[derive(Default)]
|
||||
pub struct FunctionRegistry {
|
||||
/// Maps function [names] to their respective [`DynamicClosures`].
|
||||
/// Maps function [names] to their respective [`DynamicFunctions`].
|
||||
///
|
||||
/// [names]: DynamicClosure::name
|
||||
/// [`DynamicClosures`]: DynamicClosure
|
||||
functions: HashMap<Cow<'static, str>, DynamicClosure<'static>>,
|
||||
/// [names]: DynamicFunction::name
|
||||
/// [`DynamicFunctions`]: DynamicFunction
|
||||
functions: HashMap<Cow<'static, str>, DynamicFunction<'static>>,
|
||||
}
|
||||
|
||||
impl FunctionRegistry {
|
||||
/// Attempts to register the given function.
|
||||
///
|
||||
/// This function accepts both functions/closures that satisfy [`IntoClosure`]
|
||||
/// and direct [`DynamicFunction`]/[`DynamicClosure`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicClosure<'static>`]
|
||||
/// This function accepts both functions that satisfy [`IntoFunction`]
|
||||
/// and direct [`DynamicFunction`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicFunction<'static>`]
|
||||
/// and mapped according to its [name].
|
||||
///
|
||||
/// Because the function must have a name,
|
||||
/// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead
|
||||
/// be registered using [`register_with_name`] or converted to a [`DynamicClosure`]
|
||||
/// and named using [`DynamicClosure::with_name`].
|
||||
/// be registered using [`register_with_name`] or manually converted to a [`DynamicFunction`]
|
||||
/// and named using [`DynamicFunction::with_name`].
|
||||
/// Failure to do so will result in an error being returned.
|
||||
///
|
||||
/// If a registered function with the same name already exists,
|
||||
|
@ -63,7 +60,7 @@ impl FunctionRegistry {
|
|||
/// Functions cannot be registered more than once.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{DynamicFunction, FunctionRegistrationError, FunctionRegistry, IntoClosure};
|
||||
/// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry, IntoFunction};
|
||||
/// fn add(a: i32, b: i32) -> i32 {
|
||||
/// a + b
|
||||
/// }
|
||||
|
@ -76,14 +73,14 @@ impl FunctionRegistry {
|
|||
///
|
||||
/// // Note that this simply relies on the name of the function to determine uniqueness.
|
||||
/// // You can rename the function to register a separate instance of it.
|
||||
/// let result = registry.register(add.into_closure().with_name("add2"));
|
||||
/// let result = registry.register(add.into_function().with_name("add2"));
|
||||
/// assert!(result.is_ok());
|
||||
/// ```
|
||||
///
|
||||
/// Anonymous functions and closures should be registered using [`register_with_name`] or given a name using [`DynamicClosure::with_name`].
|
||||
/// Anonymous functions and closures should be registered using [`register_with_name`] or given a name using [`DynamicFunction::with_name`].
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::func::{DynamicFunction, FunctionRegistrationError, FunctionRegistry, IntoClosure};
|
||||
/// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry, IntoFunction};
|
||||
///
|
||||
/// let anonymous = || -> i32 { 123 };
|
||||
///
|
||||
|
@ -95,12 +92,11 @@ impl FunctionRegistry {
|
|||
/// let result = registry.register_with_name("my_crate::add", |a: i32, b: i32| a + b);
|
||||
/// assert!(result.is_ok());
|
||||
///
|
||||
/// let result = registry.register((|a: i32, b: i32| a * b).into_closure().with_name("my_crate::mul"));
|
||||
/// let result = registry.register((|a: i32, b: i32| a * b).into_function().with_name("my_crate::mul"));
|
||||
/// assert!(result.is_ok());
|
||||
/// ```
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [name]: DynamicClosure::name
|
||||
/// [name]: DynamicFunction::name
|
||||
/// [`register_with_name`]: Self::register_with_name
|
||||
/// [`overwrite_registration`]: Self::overwrite_registration
|
||||
pub fn register<F, Marker>(
|
||||
|
@ -108,15 +104,15 @@ impl FunctionRegistry {
|
|||
function: F,
|
||||
) -> Result<&mut Self, FunctionRegistrationError>
|
||||
where
|
||||
F: IntoClosure<'static, Marker> + 'static,
|
||||
F: IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
let function = function.into_closure();
|
||||
let function = function.into_function();
|
||||
let name = function
|
||||
.name()
|
||||
.ok_or(FunctionRegistrationError::MissingName)?
|
||||
.clone();
|
||||
self.functions
|
||||
.try_insert(name, function.into_closure())
|
||||
.try_insert(name, function.into_function())
|
||||
.map_err(|err| FunctionRegistrationError::DuplicateName(err.entry.key().clone()))?;
|
||||
|
||||
Ok(self)
|
||||
|
@ -124,9 +120,9 @@ impl FunctionRegistry {
|
|||
|
||||
/// Attempts to register the given function with the given name.
|
||||
///
|
||||
/// This function accepts both functions/closures that satisfy [`IntoClosure`]
|
||||
/// and direct [`DynamicFunction`]/[`DynamicClosure`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicClosure<'static>`]
|
||||
/// This function accepts both functions that satisfy [`IntoFunction`]
|
||||
/// and direct [`DynamicFunction`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicFunction<'static>`]
|
||||
/// with its [name] set to the given name.
|
||||
///
|
||||
/// For named functions (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`) where a custom name is not needed,
|
||||
|
@ -145,9 +141,10 @@ impl FunctionRegistry {
|
|||
/// you could use the name, `"my_crate::add"`.
|
||||
///
|
||||
/// Another approach could be to use the [type name] of the function,
|
||||
/// however, it should be noted that anonymous functions do _not_ have unique type names.
|
||||
/// however, it should be noted that anonymous functions and closures
|
||||
///are not guaranteed to have unique type names.
|
||||
///
|
||||
/// This method is a convenience around calling [`IntoClosure::into_closure`] and [`DynamicClosure::with_name`]
|
||||
/// This method is a convenience around calling [`IntoFunction::into_function`] and [`DynamicFunction::with_name`]
|
||||
/// on the function and inserting it into the registry using the [`register`] method.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -193,8 +190,7 @@ impl FunctionRegistry {
|
|||
/// registry.register_with_name("my_function", two).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [name]: DynamicClosure::name
|
||||
/// [name]: DynamicFunction::name
|
||||
/// [`register`]: Self::register
|
||||
/// [`overwrite_registration_with_name`]: Self::overwrite_registration_with_name
|
||||
/// [type name]: std::any::type_name
|
||||
|
@ -204,23 +200,23 @@ impl FunctionRegistry {
|
|||
function: F,
|
||||
) -> Result<&mut Self, FunctionRegistrationError>
|
||||
where
|
||||
F: IntoClosure<'static, Marker> + 'static,
|
||||
F: IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
let function = function.into_closure().with_name(name);
|
||||
let function = function.into_function().with_name(name);
|
||||
self.register(function)
|
||||
}
|
||||
|
||||
/// Registers the given function, overwriting any existing registration.
|
||||
///
|
||||
/// This function accepts both functions/closures that satisfy [`IntoClosure`]
|
||||
/// and direct [`DynamicFunction`]/[`DynamicClosure`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicClosure<'static>`]
|
||||
/// This function accepts both functions that satisfy [`IntoFunction`]
|
||||
/// and direct [`DynamicFunction`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicFunction<'static>`]
|
||||
/// and mapped according to its [name].
|
||||
///
|
||||
/// Because the function must have a name,
|
||||
/// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead
|
||||
/// be registered using [`overwrite_registration_with_name`] or converted to a [`DynamicClosure`]
|
||||
/// and named using [`DynamicClosure::with_name`].
|
||||
/// be registered using [`overwrite_registration_with_name`] or manually converted to a [`DynamicFunction`]
|
||||
/// and named using [`DynamicFunction::with_name`].
|
||||
/// Failure to do so will result in an error being returned.
|
||||
///
|
||||
/// To avoid overwriting existing registrations,
|
||||
|
@ -228,18 +224,17 @@ impl FunctionRegistry {
|
|||
///
|
||||
/// Returns the previous function with the same name, if any.
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [name]: DynamicClosure::name
|
||||
/// [name]: DynamicFunction::name
|
||||
/// [`overwrite_registration_with_name`]: Self::overwrite_registration_with_name
|
||||
/// [`register`]: Self::register
|
||||
pub fn overwrite_registration<F, Marker>(
|
||||
&mut self,
|
||||
function: F,
|
||||
) -> Result<Option<DynamicClosure<'static>>, FunctionRegistrationError>
|
||||
) -> Result<Option<DynamicFunction<'static>>, FunctionRegistrationError>
|
||||
where
|
||||
F: IntoClosure<'static, Marker> + 'static,
|
||||
F: IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
let function = function.into_closure();
|
||||
let function = function.into_function();
|
||||
let name = function
|
||||
.name()
|
||||
.ok_or(FunctionRegistrationError::MissingName)?
|
||||
|
@ -250,33 +245,32 @@ impl FunctionRegistry {
|
|||
|
||||
/// Registers the given function, overwriting any existing registration.
|
||||
///
|
||||
/// This function accepts both functions/closures that satisfy [`IntoClosure`]
|
||||
/// and direct [`DynamicFunction`]/[`DynamicClosure`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicClosure<'static>`]
|
||||
/// This function accepts both functions that satisfy [`IntoFunction`]
|
||||
/// and direct [`DynamicFunction`] instances.
|
||||
/// The given function will internally be stored as a [`DynamicFunction<'static>`]
|
||||
/// with its [name] set to the given name.
|
||||
///
|
||||
/// Functions are mapped according to their name.
|
||||
/// To avoid overwriting existing registrations,
|
||||
/// it's recommended to use the [`register_with_name`] method instead.
|
||||
///
|
||||
/// This method is a convenience around calling [`IntoClosure::into_closure`] and [`DynamicClosure::with_name`]
|
||||
/// This method is a convenience around calling [`IntoFunction::into_function`] and [`DynamicFunction::with_name`]
|
||||
/// on the function and inserting it into the registry using the [`overwrite_registration`] method.
|
||||
///
|
||||
/// Returns the previous function with the same name, if any.
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [name]: DynamicClosure::name
|
||||
/// [name]: DynamicFunction::name
|
||||
/// [`register_with_name`]: Self::register_with_name
|
||||
/// [`overwrite_registration`]: Self::overwrite_registration
|
||||
pub fn overwrite_registration_with_name<F, Marker>(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
function: F,
|
||||
) -> Option<DynamicClosure<'static>>
|
||||
) -> Option<DynamicFunction<'static>>
|
||||
where
|
||||
F: IntoClosure<'static, Marker> + 'static,
|
||||
F: IntoFunction<'static, Marker> + 'static,
|
||||
{
|
||||
let function = function.into_closure().with_name(name);
|
||||
let function = function.into_function().with_name(name);
|
||||
match self.overwrite_registration(function) {
|
||||
Ok(existing) => existing,
|
||||
Err(FunctionRegistrationError::MissingName) => {
|
||||
|
@ -288,31 +282,31 @@ impl FunctionRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get a reference to a registered function or closure by [name].
|
||||
/// Get a reference to a registered function by [name].
|
||||
///
|
||||
/// [name]: DynamicClosure::name
|
||||
pub fn get(&self, name: &str) -> Option<&DynamicClosure<'static>> {
|
||||
/// [name]: DynamicFunction::name
|
||||
pub fn get(&self, name: &str) -> Option<&DynamicFunction<'static>> {
|
||||
self.functions.get(name)
|
||||
}
|
||||
|
||||
/// Returns `true` if a function or closure with the given [name] is registered.
|
||||
/// Returns `true` if a function with the given [name] is registered.
|
||||
///
|
||||
/// [name]: DynamicClosure::name
|
||||
/// [name]: DynamicFunction::name
|
||||
pub fn contains(&self, name: &str) -> bool {
|
||||
self.functions.contains_key(name)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all registered functions/closures.
|
||||
pub fn iter(&self) -> impl ExactSizeIterator<Item = &DynamicClosure<'static>> {
|
||||
/// Returns an iterator over all registered functions.
|
||||
pub fn iter(&self) -> impl ExactSizeIterator<Item = &DynamicFunction<'static>> {
|
||||
self.functions.values()
|
||||
}
|
||||
|
||||
/// Returns the number of registered functions/closures.
|
||||
/// Returns the number of registered functions.
|
||||
pub fn len(&self) -> usize {
|
||||
self.functions.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if no functions or closures are registered.
|
||||
/// Returns `true` if no functions are registered.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.functions.is_empty()
|
||||
}
|
||||
|
@ -407,7 +401,7 @@ mod tests {
|
|||
let value = 123;
|
||||
let foo = move || -> i32 { value };
|
||||
|
||||
let function = foo.into_closure().with_name("custom_name");
|
||||
let function = foo.into_function().with_name("custom_name");
|
||||
|
||||
let mut registry = FunctionRegistry::default();
|
||||
registry.register(function).unwrap();
|
||||
|
@ -494,6 +488,6 @@ mod tests {
|
|||
registry.register_with_name("foo", foo).unwrap();
|
||||
|
||||
let debug = format!("{:?}", registry);
|
||||
assert_eq!(debug, "{DynamicClosure(fn foo() -> i32)}");
|
||||
assert_eq!(debug, "{DynamicFunction(fn foo() -> i32)}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::PartialReflect;
|
||||
|
||||
/// The return type of a [`DynamicFunction`] or [`DynamicClosure`].
|
||||
/// The return type of a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||
///
|
||||
/// [`DynamicFunction`]: crate::func::DynamicFunction
|
||||
/// [`DynamicClosure`]: crate::func::DynamicClosure
|
||||
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
|
||||
#[derive(Debug)]
|
||||
pub enum Return<'a> {
|
||||
/// The function returns nothing (i.e. it returns `()`).
|
||||
|
@ -62,14 +62,15 @@ impl<'a> Return<'a> {
|
|||
/// A trait for types that can be converted into a [`Return`] value.
|
||||
///
|
||||
/// This trait exists so that types can be automatically converted into a [`Return`]
|
||||
/// by [`IntoFunction`].
|
||||
/// by [`ReflectFn`] and [`ReflectFnMut`].
|
||||
///
|
||||
/// This trait is used instead of a blanket [`Into`] implementation due to coherence issues:
|
||||
/// we can't implement `Into<Return>` for both `T` and `&T`/`&mut T`.
|
||||
///
|
||||
/// This trait is automatically implemented when using the `Reflect` [derive macro].
|
||||
///
|
||||
/// [`IntoFunction`]: crate::func::IntoFunction
|
||||
/// [`ReflectFn`]: crate::func::ReflectFn
|
||||
/// [`ReflectFnMut`]: crate::func::ReflectFnMut
|
||||
/// [derive macro]: derive@crate::Reflect
|
||||
pub trait IntoReturn {
|
||||
/// Converts [`Self`] into a [`Return`] value.
|
||||
|
|
|
@ -574,7 +574,7 @@ pub mod prelude {
|
|||
};
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
pub use crate::func::IntoFunction;
|
||||
pub use crate::func::{IntoFunction, IntoFunctionMut};
|
||||
}
|
||||
|
||||
pub use array::*;
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
//! processing deserialized reflection data, or even just storing type-erased versions of your functions.
|
||||
|
||||
use bevy::reflect::func::{
|
||||
ArgList, DynamicClosure, DynamicClosureMut, DynamicFunction, FunctionError, FunctionInfo,
|
||||
IntoClosure, IntoClosureMut, IntoFunction, Return,
|
||||
ArgList, DynamicFunction, DynamicFunctionMut, FunctionError, FunctionInfo, IntoFunction,
|
||||
IntoFunctionMut, Return,
|
||||
};
|
||||
use bevy::reflect::{PartialReflect, Reflect};
|
||||
|
||||
|
@ -37,7 +37,7 @@ fn main() {
|
|||
// Luckily, Bevy's reflection crate comes with a set of tools for doing just that!
|
||||
// We do this by first converting our function into the reflection-based `DynamicFunction` type
|
||||
// using the `IntoFunction` trait.
|
||||
let function: DynamicFunction = dbg!(add.into_function());
|
||||
let function: DynamicFunction<'static> = dbg!(add.into_function());
|
||||
|
||||
// This time, you'll notice that `DynamicFunction` doesn't take any information about the function's arguments or return value.
|
||||
// This is because `DynamicFunction` checks the types of the arguments and return value at runtime.
|
||||
|
@ -56,27 +56,28 @@ fn main() {
|
|||
assert_eq!(value.try_take::<i32>().unwrap(), 4);
|
||||
|
||||
// The same can also be done for closures that capture references to their environment.
|
||||
// Closures that capture their environment immutably can be converted into a `DynamicClosure`
|
||||
// using the `IntoClosure` trait.
|
||||
// Closures that capture their environment immutably can be converted into a `DynamicFunction`
|
||||
// using the `IntoFunction` trait.
|
||||
let minimum = 5;
|
||||
let clamp = |value: i32| value.max(minimum);
|
||||
|
||||
let function: DynamicClosure = dbg!(clamp.into_closure());
|
||||
let function: DynamicFunction = dbg!(clamp.into_function());
|
||||
let args = dbg!(ArgList::new().push_owned(2_i32));
|
||||
let return_value = dbg!(function.call(args).unwrap());
|
||||
let value: Box<dyn PartialReflect> = return_value.unwrap_owned();
|
||||
assert_eq!(value.try_take::<i32>().unwrap(), 5);
|
||||
|
||||
// We can also handle closures that capture their environment mutably
|
||||
// using the `IntoClosureMut` trait.
|
||||
// using the `IntoFunctionMut` trait.
|
||||
let mut count = 0;
|
||||
let increment = |amount: i32| count += amount;
|
||||
|
||||
let closure: DynamicClosureMut = dbg!(increment.into_closure_mut());
|
||||
let closure: DynamicFunctionMut = dbg!(increment.into_function_mut());
|
||||
let args = dbg!(ArgList::new().push_owned(5_i32));
|
||||
// Because `DynamicClosureMut` mutably borrows `total`,
|
||||
|
||||
// Because `DynamicFunctionMut` mutably borrows `total`,
|
||||
// it will need to be dropped before `total` can be accessed again.
|
||||
// This can be done manually with `drop(closure)` or by using the `DynamicClosureMut::call_once` method.
|
||||
// This can be done manually with `drop(closure)` or by using the `DynamicFunctionMut::call_once` method.
|
||||
dbg!(closure.call_once(args).unwrap());
|
||||
assert_eq!(count, 5);
|
||||
|
||||
|
|
Loading…
Reference in a new issue