Path: blob/main/crates/bevy_reflect/src/func/registry.rs
6599 views
use alloc::borrow::Cow;1use bevy_platform::{2collections::HashMap,3sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},4};5use core::fmt::Debug;67use crate::func::{8ArgList, DynamicFunction, FunctionRegistrationError, FunctionResult, IntoFunction,9};1011/// A registry of [reflected functions].12///13/// This is the function-equivalent to the [`TypeRegistry`].14///15/// All functions must be `'static` as they are stored as [`DynamicFunction<'static>`].16///17/// [reflected functions]: crate::func18/// [`TypeRegistry`]: crate::TypeRegistry19#[derive(Default)]20pub struct FunctionRegistry {21/// Maps function [names] to their respective [`DynamicFunctions`].22///23/// [names]: DynamicFunction::name24/// [`DynamicFunctions`]: DynamicFunction25functions: HashMap<Cow<'static, str>, DynamicFunction<'static>>,26}2728impl FunctionRegistry {29/// Attempts to register the given function.30///31/// This function accepts both functions that satisfy [`IntoFunction`]32/// and direct [`DynamicFunction`] instances.33/// The given function will internally be stored as a [`DynamicFunction<'static>`]34/// and mapped according to its [name].35///36/// Because the function must have a name,37/// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead38/// be registered using [`register_with_name`] or manually converted to a [`DynamicFunction`]39/// and named using [`DynamicFunction::with_name`].40/// Failure to do so will result in an error being returned.41///42/// If a registered function with the same name already exists,43/// it will not be registered again and an error will be returned.44/// To register the function anyway, overwriting any existing registration,45/// use [`overwrite_registration`] instead.46///47/// # Examples48///49/// ```50/// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry};51/// fn add(a: i32, b: i32) -> i32 {52/// a + b53/// }54///55/// # fn main() -> Result<(), FunctionRegistrationError> {56/// let mut registry = FunctionRegistry::default();57/// registry.register(add)?;58/// # Ok(())59/// # }60/// ```61///62/// Functions cannot be registered more than once.63///64/// ```65/// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry, IntoFunction};66/// fn add(a: i32, b: i32) -> i32 {67/// a + b68/// }69///70/// let mut registry = FunctionRegistry::default();71/// registry.register(add).unwrap();72///73/// let result = registry.register(add);74/// assert!(matches!(result, Err(FunctionRegistrationError::DuplicateName(_))));75///76/// // Note that this simply relies on the name of the function to determine uniqueness.77/// // You can rename the function to register a separate instance of it.78/// let result = registry.register(add.into_function().with_name("add2"));79/// assert!(result.is_ok());80/// ```81///82/// Anonymous functions and closures should be registered using [`register_with_name`] or given a name using [`DynamicFunction::with_name`].83///84/// ```85/// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry, IntoFunction};86///87/// let anonymous = || -> i32 { 123 };88///89/// let mut registry = FunctionRegistry::default();90///91/// let result = registry.register(|a: i32, b: i32| a + b);92/// assert!(matches!(result, Err(FunctionRegistrationError::MissingName)));93///94/// let result = registry.register_with_name("my_crate::add", |a: i32, b: i32| a + b);95/// assert!(result.is_ok());96///97/// let result = registry.register((|a: i32, b: i32| a * b).into_function().with_name("my_crate::mul"));98/// assert!(result.is_ok());99/// ```100///101/// [name]: DynamicFunction::name102/// [`register_with_name`]: Self::register_with_name103/// [`overwrite_registration`]: Self::overwrite_registration104pub fn register<F, Marker>(105&mut self,106function: F,107) -> Result<&mut Self, FunctionRegistrationError>108where109F: IntoFunction<'static, Marker> + 'static,110{111let function = function.into_function();112let name = function113.name()114.ok_or(FunctionRegistrationError::MissingName)?115.clone();116self.functions117.try_insert(name, function.into_function())118.map_err(|err| FunctionRegistrationError::DuplicateName(err.entry.key().clone()))?;119120Ok(self)121}122123/// Attempts to register the given function with the given name.124///125/// This function accepts both functions that satisfy [`IntoFunction`]126/// and direct [`DynamicFunction`] instances.127/// The given function will internally be stored as a [`DynamicFunction<'static>`]128/// with its [name] set to the given name.129///130/// For named functions (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`) where a custom name is not needed,131/// it's recommended to use [`register`] instead as the generated name is guaranteed to be unique.132///133/// If a registered function with the same name already exists,134/// it will not be registered again and an error will be returned.135/// To register the function anyway, overwriting any existing registration,136/// use [`overwrite_registration_with_name`] instead.137///138/// To avoid conflicts, it's recommended to use a unique name for the function.139/// This can be achieved by "namespacing" the function with a unique identifier,140/// such as the name of your crate.141///142/// For example, to register a function, `add`, from a crate, `my_crate`,143/// you could use the name, `"my_crate::add"`.144///145/// Another approach could be to use the [type name] of the function,146/// however, it should be noted that anonymous functions and closures147/// are not guaranteed to have unique type names.148///149/// This method is a convenience around calling [`IntoFunction::into_function`] and [`DynamicFunction::with_name`]150/// on the function and inserting it into the registry using the [`register`] method.151///152/// # Examples153///154/// ```155/// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry};156/// # fn main() -> Result<(), FunctionRegistrationError> {157/// fn mul(a: i32, b: i32) -> i32 {158/// a * b159/// }160///161/// let div = |a: i32, b: i32| a / b;162///163/// let mut registry = FunctionRegistry::default();164/// registry165/// // Registering an anonymous function with a unique name166/// .register_with_name("my_crate::add", |a: i32, b: i32| {167/// a + b168/// })?169/// // Registering an existing function with its type name170/// .register_with_name(core::any::type_name_of_val(&mul), mul)?171/// // Registering an existing function with a custom name172/// .register_with_name("my_crate::mul", mul)?;173///174/// // Be careful not to register anonymous functions with their type name.175/// // This code works but registers the function with a non-unique name like `foo::bar::{{closure}}`176/// registry.register_with_name(core::any::type_name_of_val(&div), div)?;177/// # Ok(())178/// # }179/// ```180///181/// Names must be unique.182///183/// ```should_panic184/// # use bevy_reflect::func::FunctionRegistry;185/// fn one() {}186/// fn two() {}187///188/// let mut registry = FunctionRegistry::default();189/// registry.register_with_name("my_function", one).unwrap();190///191/// // Panic! A function has already been registered with the name "my_function"192/// registry.register_with_name("my_function", two).unwrap();193/// ```194///195/// [name]: DynamicFunction::name196/// [`register`]: Self::register197/// [`overwrite_registration_with_name`]: Self::overwrite_registration_with_name198/// [type name]: core::any::type_name199pub fn register_with_name<F, Marker>(200&mut self,201name: impl Into<Cow<'static, str>>,202function: F,203) -> Result<&mut Self, FunctionRegistrationError>204where205F: IntoFunction<'static, Marker> + 'static,206{207let function = function.into_function().with_name(name);208self.register(function)209}210211/// Registers the given function, overwriting any existing registration.212///213/// This function accepts both functions that satisfy [`IntoFunction`]214/// and direct [`DynamicFunction`] instances.215/// The given function will internally be stored as a [`DynamicFunction<'static>`]216/// and mapped according to its [name].217///218/// Because the function must have a name,219/// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead220/// be registered using [`overwrite_registration_with_name`] or manually converted to a [`DynamicFunction`]221/// and named using [`DynamicFunction::with_name`].222/// Failure to do so will result in an error being returned.223///224/// To avoid overwriting existing registrations,225/// it's recommended to use the [`register`] method instead.226///227/// Returns the previous function with the same name, if any.228///229/// [name]: DynamicFunction::name230/// [`overwrite_registration_with_name`]: Self::overwrite_registration_with_name231/// [`register`]: Self::register232pub fn overwrite_registration<F, Marker>(233&mut self,234function: F,235) -> Result<Option<DynamicFunction<'static>>, FunctionRegistrationError>236where237F: IntoFunction<'static, Marker> + 'static,238{239let function = function.into_function();240let name = function241.name()242.ok_or(FunctionRegistrationError::MissingName)?243.clone();244245Ok(self.functions.insert(name, function))246}247248/// Registers the given function, overwriting any existing registration.249///250/// This function accepts both functions that satisfy [`IntoFunction`]251/// and direct [`DynamicFunction`] instances.252/// The given function will internally be stored as a [`DynamicFunction<'static>`]253/// with its [name] set to the given name.254///255/// Functions are mapped according to their name.256/// To avoid overwriting existing registrations,257/// it's recommended to use the [`register_with_name`] method instead.258///259/// This method is a convenience around calling [`IntoFunction::into_function`] and [`DynamicFunction::with_name`]260/// on the function and inserting it into the registry using the [`overwrite_registration`] method.261///262/// Returns the previous function with the same name, if any.263///264/// [name]: DynamicFunction::name265/// [`register_with_name`]: Self::register_with_name266/// [`overwrite_registration`]: Self::overwrite_registration267pub fn overwrite_registration_with_name<F, Marker>(268&mut self,269name: impl Into<Cow<'static, str>>,270function: F,271) -> Option<DynamicFunction<'static>>272where273F: IntoFunction<'static, Marker> + 'static,274{275let function = function.into_function().with_name(name);276match self.overwrite_registration(function) {277Ok(existing) => existing,278Err(FunctionRegistrationError::MissingName) => {279unreachable!("the function should have a name")280}281Err(FunctionRegistrationError::DuplicateName(_)) => {282unreachable!("should overwrite functions with the same name")283}284}285}286287/// Calls the function with the given [name] and [args].288///289/// Returns `None` if no function with the given name is registered.290/// Otherwise, returns the result of the function call.291///292/// [name]: DynamicFunction::name293/// [args]: ArgList294pub fn call<'a>(&self, name: &str, args: ArgList<'a>) -> Option<FunctionResult<'a>> {295let func = self.get(name)?;296Some(func.call(args))297}298299/// Get a reference to a registered function by [name].300///301/// [name]: DynamicFunction::name302pub fn get(&self, name: &str) -> Option<&DynamicFunction<'static>> {303self.functions.get(name)304}305306/// Returns `true` if a function with the given [name] is registered.307///308/// [name]: DynamicFunction::name309pub fn contains(&self, name: &str) -> bool {310self.functions.contains_key(name)311}312313/// Returns an iterator over all registered functions.314pub fn iter(&self) -> impl ExactSizeIterator<Item = &DynamicFunction<'static>> {315self.functions.values()316}317318/// Returns the number of registered functions.319pub fn len(&self) -> usize {320self.functions.len()321}322323/// Returns `true` if no functions are registered.324pub fn is_empty(&self) -> bool {325self.functions.is_empty()326}327}328329impl Debug for FunctionRegistry {330fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {331f.debug_set().entries(self.functions.values()).finish()332}333}334335/// A synchronized wrapper around a [`FunctionRegistry`].336#[derive(Clone, Default, Debug)]337pub struct FunctionRegistryArc {338/// The wrapped [`FunctionRegistry`].339pub internal: Arc<RwLock<FunctionRegistry>>,340}341342impl FunctionRegistryArc {343/// Takes a read lock on the underlying [`FunctionRegistry`].344pub fn read(&self) -> RwLockReadGuard<'_, FunctionRegistry> {345self.internal.read().unwrap_or_else(PoisonError::into_inner)346}347348/// Takes a write lock on the underlying [`FunctionRegistry`].349pub fn write(&self) -> RwLockWriteGuard<'_, FunctionRegistry> {350self.internal351.write()352.unwrap_or_else(PoisonError::into_inner)353}354}355356#[cfg(test)]357mod tests {358use super::*;359use crate::func::{ArgList, IntoFunction};360use alloc::format;361362#[test]363fn should_register_function() {364fn foo() -> i32 {365123366}367368let mut registry = FunctionRegistry::default();369registry.register(foo).unwrap();370371let function = registry.get(core::any::type_name_of_val(&foo)).unwrap();372let value = function.call(ArgList::new()).unwrap().unwrap_owned();373assert_eq!(value.try_downcast_ref::<i32>(), Some(&123));374}375376#[test]377fn should_register_anonymous_function() {378let mut registry = FunctionRegistry::default();379registry.register_with_name("foo", || 123_i32).unwrap();380381let function = registry.get("foo").unwrap();382let value = function.call(ArgList::new()).unwrap().unwrap_owned();383assert_eq!(value.try_downcast_ref::<i32>(), Some(&123));384}385386#[test]387fn should_register_closure() {388let value = 123;389let foo = move || -> i32 { value };390391let mut registry = FunctionRegistry::default();392registry.register_with_name("foo", foo).unwrap();393394let function = registry.get("foo").unwrap();395let value = function.call(ArgList::new()).unwrap().unwrap_owned();396assert_eq!(value.try_downcast_ref::<i32>(), Some(&123));397}398399#[test]400fn should_register_dynamic_function() {401fn foo() -> i32 {402123403}404405let function = foo.into_function().with_name("custom_name");406407let mut registry = FunctionRegistry::default();408registry.register(function).unwrap();409410let function = registry.get("custom_name").unwrap();411let value = function.call(ArgList::new()).unwrap().unwrap_owned();412assert_eq!(value.try_downcast_ref::<i32>(), Some(&123));413}414415#[test]416fn should_register_dynamic_closure() {417let value = 123;418let foo = move || -> i32 { value };419420let function = foo.into_function().with_name("custom_name");421422let mut registry = FunctionRegistry::default();423registry.register(function).unwrap();424425let function = registry.get("custom_name").unwrap();426let value = function.call(ArgList::new()).unwrap().unwrap_owned();427assert_eq!(value.try_downcast_ref::<i32>(), Some(&123));428}429430#[test]431fn should_only_register_function_once() {432fn foo() -> i32 {433123434}435436fn bar() -> i32 {437321438}439440let name = core::any::type_name_of_val(&foo);441442let mut registry = FunctionRegistry::default();443registry.register(foo).unwrap();444let result = registry.register(bar.into_function().with_name(name));445446assert!(matches!(447result,448Err(FunctionRegistrationError::DuplicateName(_))449));450assert_eq!(registry.len(), 1);451452let function = registry.get(name).unwrap();453let value = function.call(ArgList::new()).unwrap().unwrap_owned();454assert_eq!(value.try_downcast_ref::<i32>(), Some(&123));455}456457#[test]458fn should_allow_overwriting_registration() {459fn foo() -> i32 {460123461}462463fn bar() -> i32 {464321465}466467let name = core::any::type_name_of_val(&foo);468469let mut registry = FunctionRegistry::default();470registry.register(foo).unwrap();471registry472.overwrite_registration(bar.into_function().with_name(name))473.unwrap();474475assert_eq!(registry.len(), 1);476477let function = registry.get(name).unwrap();478let value = function.call(ArgList::new()).unwrap().unwrap_owned();479assert_eq!(value.try_downcast_ref::<i32>(), Some(&321));480}481482#[test]483fn should_call_function_via_registry() {484fn add(a: i32, b: i32) -> i32 {485a + b486}487488let mut registry = FunctionRegistry::default();489registry.register(add).unwrap();490491let args = ArgList::new().with_owned(25_i32).with_owned(75_i32);492let result = registry493.call(core::any::type_name_of_val(&add), args)494.unwrap();495let value = result.unwrap().unwrap_owned();496assert_eq!(value.try_downcast_ref::<i32>(), Some(&100));497}498499#[test]500fn should_error_on_missing_name() {501let foo = || -> i32 { 123 };502503let function = foo.into_function();504505let mut registry = FunctionRegistry::default();506let result = registry.register(function);507508assert!(matches!(509result,510Err(FunctionRegistrationError::MissingName)511));512}513514#[test]515fn should_debug_function_registry() {516fn foo() -> i32 {517123518}519520let mut registry = FunctionRegistry::default();521registry.register_with_name("foo", foo).unwrap();522523let debug = format!("{registry:?}");524assert_eq!(debug, "{DynamicFunction(fn foo() -> i32)}");525}526}527528529