Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,16 +605,16 @@ impl App {

/// Registers the given function into the [`AppFunctionRegistry`] resource.
///
/// The given function will internally be stored as a [`DynamicFunction`]
/// The given function will internally be stored as a [`DynamicClosure`]
/// and mapped according to its [name].
///
/// Because the function must have a name,
/// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) must instead
/// 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 an error being returned.
/// 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`].
/// Failure to do so will result in a panic.
///
/// Only functions that implement [`IntoFunction`] may be registered via this method.
/// Only types that implement [`IntoClosure`] may be registered via this method.
///
/// See [`FunctionRegistry::register`] for more information.
///
Expand Down Expand Up @@ -650,7 +650,7 @@ impl App {
/// .register_function(add);
/// ```
///
/// Anonymous functions should be registered using [`register_function_with_name`] or given a name using [`DynamicFunction::with_name`].
/// Anonymous functions and closures should be registered using [`register_function_with_name`] or given a name using [`DynamicClosure::with_name`].
///
/// ```should_panic
/// use bevy_app::App;
Expand All @@ -660,21 +660,21 @@ impl App {
/// ```
///
/// [`register_function_with_name`]: Self::register_function_with_name
/// [`DynamicFunction`]: bevy_reflect::func::DynamicFunction
/// [`DynamicClosure`]: bevy_reflect::func::DynamicClosure
/// [name]: bevy_reflect::func::FunctionInfo::name
/// [`DynamicFunction::with_name`]: bevy_reflect::func::DynamicFunction::with_name
/// [`IntoFunction`]: bevy_reflect::func::IntoFunction
/// [`DynamicClosure::with_name`]: bevy_reflect::func::DynamicClosure::with_name
/// [`IntoClosure`]: bevy_reflect::func::IntoClosure
/// [`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::IntoFunction<Marker> + 'static,
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
{
self.main_mut().register_function(function);
self
}

/// Registers the given function into the [`AppFunctionRegistry`] resource using the given name.
/// Registers the given function or closure into the [`AppFunctionRegistry`] resource using the given name.
///
/// To avoid conflicts, it's recommended to use a unique name for the function.
/// This can be achieved by "namespacing" the function with a unique identifier,
Expand All @@ -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 functions that implement [`IntoFunction`] may be registered via this method.
/// Only types that implement [`IntoClosure`] may be registered via this method.
///
/// See [`FunctionRegistry::register_with_name`] for more information.
///
Expand Down Expand Up @@ -718,7 +718,7 @@ impl App {
/// // Registering an existing function with a custom name
/// .register_function_with_name("my_crate::mul", mul)
/// // Be careful not to register anonymous functions with their type name.
/// // This code works but registers the function with the non-unique name of `fn(i32, i32) -> i32`
/// // This code works but registers the function with a non-unique name like `foo::bar::{{closure}}`
/// .register_function_with_name(std::any::type_name_of_val(&div), div);
/// ```
///
Expand All @@ -738,7 +738,7 @@ impl App {
///
/// [type name]: std::any::type_name
/// [`register_function`]: Self::register_function
/// [`IntoFunction`]: bevy_reflect::func::IntoFunction
/// [`IntoClosure`]: bevy_reflect::func::IntoClosure
/// [`FunctionRegistry::register_with_name`]: bevy_reflect::func::FunctionRegistry::register_with_name
#[cfg(feature = "reflect_functions")]
pub fn register_function_with_name<F, Marker>(
Expand All @@ -747,7 +747,7 @@ impl App {
function: F,
) -> &mut Self
where
F: bevy_reflect::func::IntoFunction<Marker> + 'static,
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
{
self.main_mut().register_function_with_name(name, function);
self
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_app/src/sub_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::IntoFunction<Marker> + 'static,
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
{
let registry = self.world.resource_mut::<AppFunctionRegistry>();
registry.write().register(function).unwrap();
Expand All @@ -428,7 +428,7 @@ impl SubApp {
function: F,
) -> &mut Self
where
F: bevy_reflect::func::IntoFunction<Marker> + 'static,
F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static,
{
let registry = self.world.resource_mut::<AppFunctionRegistry>();
registry.write().register_with_name(name, function).unwrap();
Expand Down
69 changes: 58 additions & 11 deletions crates/bevy_reflect/src/func/closures/dynamic_closure.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use alloc::borrow::Cow;
use core::fmt::{Debug, Formatter};

use crate::func::args::{ArgInfo, ArgList};
use crate::func::info::FunctionInfo;
use crate::func::{FunctionResult, IntoClosure, ReturnInfo};
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.
///
Expand Down Expand Up @@ -42,12 +44,9 @@ use crate::func::{FunctionResult, IntoClosure, ReturnInfo};
/// // Check the result:
/// assert_eq!(value.try_take::<String>().unwrap(), "Hello, world!!!");
/// ```
///
/// [`DynamicClosureMut`]: crate::func::closures::DynamicClosureMut
/// [`DynamicFunction`]: crate::func::DynamicFunction
pub struct DynamicClosure<'env> {
info: FunctionInfo,
func: Box<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + 'env>,
pub(super) info: FunctionInfo,
pub(super) func: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>,
}

impl<'env> DynamicClosure<'env> {
Expand All @@ -57,13 +56,13 @@ impl<'env> DynamicClosure<'env> {
///
/// 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> + 'env>(
pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>(
func: F,
info: FunctionInfo,
) -> Self {
Self {
info,
func: Box::new(func),
func: Arc::new(func),
}
}

Expand Down Expand Up @@ -160,13 +159,39 @@ impl<'env> Debug for DynamicClosure<'env> {
}
}

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::*;
Expand All @@ -190,4 +215,26 @@ mod tests {
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!");
}
}
25 changes: 21 additions & 4 deletions crates/bevy_reflect/src/func/closures/dynamic_closure_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::fmt::{Debug, Formatter};

use crate::func::args::{ArgInfo, ArgList};
use crate::func::info::FunctionInfo;
use crate::func::{FunctionResult, IntoClosureMut, ReturnInfo};
use crate::func::{DynamicClosure, DynamicFunction, FunctionResult, IntoClosureMut, ReturnInfo};

/// A dynamic representation of a Rust closure.
///
Expand Down Expand Up @@ -51,9 +51,6 @@ use crate::func::{FunctionResult, IntoClosureMut, ReturnInfo};
/// drop(func);
/// assert_eq!(list, vec![1, -2, 3]);
/// ```
///
/// [`DynamicClosure`]: crate::func::closures::DynamicClosure
/// [`DynamicFunction`]: crate::func::DynamicFunction
pub struct DynamicClosureMut<'env> {
info: FunctionInfo,
func: Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>,
Expand Down Expand Up @@ -202,6 +199,26 @@ impl<'env> Debug for DynamicClosureMut<'env> {
}
}

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> {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/func/closures/into_closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub trait IntoClosure<'env, Marker> {

impl<'env, F, Marker1, Marker2> IntoClosure<'env, (Marker1, Marker2)> for F
where
F: ReflectFn<'env, Marker1> + TypedFunction<Marker2> + 'env,
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())
Expand Down
24 changes: 20 additions & 4 deletions crates/bevy_reflect/src/func/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use std::sync::Arc;

use crate::func::args::{ArgInfo, ArgList};
use crate::func::info::FunctionInfo;
use crate::func::{FunctionResult, IntoFunction, ReturnInfo};
use crate::func::{
DynamicClosure, DynamicClosureMut, FunctionResult, IntoClosure, IntoClosureMut, IntoFunction,
ReturnInfo,
};

/// A dynamic representation of a Rust function.
///
Expand Down Expand Up @@ -88,11 +91,10 @@ use crate::func::{FunctionResult, IntoFunction, ReturnInfo};
/// assert_eq!(list, vec!["Hello, World!!!"]);
/// ```
///
/// [`DynamicClosure`]: crate::func::DynamicClosure
/// [module-level documentation]: crate::func
pub struct DynamicFunction {
info: FunctionInfo,
func: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'static>,
pub(super) info: FunctionInfo,
pub(super) func: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'static>,
}

impl DynamicFunction {
Expand Down Expand Up @@ -221,6 +223,20 @@ impl IntoFunction<()> for DynamicFunction {
}
}

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::*;
Expand Down
Loading