use alloc::borrow::Cow;
use bevy_utils::all_tuples;
use crate::{
func::args::{ArgInfo, GetOwnership, Ownership},
type_info::impl_type_methods,
Type, TypePath,
};
#[derive(Debug, Clone)]
pub struct FunctionInfo {
name: Option<Cow<'static, str>>,
args: Vec<ArgInfo>,
return_info: ReturnInfo,
}
impl FunctionInfo {
pub fn named(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: Some(name.into()),
args: Vec::new(),
return_info: ReturnInfo::new::<()>(),
}
}
pub fn anonymous() -> Self {
Self {
name: None,
args: Vec::new(),
return_info: ReturnInfo::new::<()>(),
}
}
pub fn from<F, Marker>(function: &F) -> Self
where
F: TypedFunction<Marker>,
{
function.get_function_info()
}
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_arg<T: TypePath + GetOwnership>(
mut self,
name: impl Into<Cow<'static, str>>,
) -> Self {
let index = self.args.len();
self.args.push(ArgInfo::new::<T>(index).with_name(name));
self
}
pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self {
self.args = args;
self
}
pub fn with_return<T: TypePath + GetOwnership>(mut self) -> Self {
self.return_info = ReturnInfo::new::<T>();
self
}
pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self {
self.return_info = return_info;
self
}
pub fn name(&self) -> Option<&Cow<'static, str>> {
self.name.as_ref()
}
pub fn args(&self) -> &[ArgInfo] {
&self.args
}
pub fn arg_count(&self) -> usize {
self.args.len()
}
pub fn return_info(&self) -> &ReturnInfo {
&self.return_info
}
}
#[derive(Debug, Clone)]
pub struct ReturnInfo {
ty: Type,
ownership: Ownership,
}
impl ReturnInfo {
pub fn new<T: TypePath + GetOwnership>() -> Self {
Self {
ty: Type::of::<T>(),
ownership: T::ownership(),
}
}
impl_type_methods!(ty);
pub fn ownership(&self) -> Ownership {
self.ownership
}
}
pub trait TypedFunction<Marker> {
fn function_info() -> FunctionInfo;
fn get_function_info(&self) -> FunctionInfo {
Self::function_info()
}
}
macro_rules! impl_typed_function {
($(($Arg:ident, $arg:ident)),*) => {
impl<$($Arg,)* ReturnType, Function> TypedFunction<fn($($Arg),*) -> [ReturnType]> for Function
where
$($Arg: TypePath + GetOwnership,)*
ReturnType: TypePath + GetOwnership,
Function: FnMut($($Arg),*) -> ReturnType,
{
fn function_info() -> FunctionInfo {
create_info::<Function>()
.with_args({
#[allow(unused_mut)]
let mut _index = 0;
vec![
$(ArgInfo::new::<$Arg>({
_index += 1;
_index - 1
}),)*
]
})
.with_return_info(ReturnInfo::new::<ReturnType>())
}
}
impl<Receiver, $($Arg,)* ReturnType, Function> TypedFunction<fn(&Receiver, $($Arg),*) -> &ReturnType> for Function
where
for<'a> &'a Receiver: TypePath + GetOwnership,
$($Arg: TypePath + GetOwnership,)*
for<'a> &'a ReturnType: TypePath + GetOwnership,
Function: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a ReturnType,
{
fn function_info() -> $crate::func::FunctionInfo {
create_info::<Function>()
.with_args({
#[allow(unused_mut)]
let mut _index = 1;
vec![
ArgInfo::new::<&Receiver>(0),
$($crate::func::args::ArgInfo::new::<$Arg>({
_index += 1;
_index - 1
}),)*
]
})
.with_return_info(ReturnInfo::new::<&ReturnType>())
}
}
impl<Receiver, $($Arg,)* ReturnType, Function> TypedFunction<fn(&mut Receiver, $($Arg),*) -> &mut ReturnType> for Function
where
for<'a> &'a mut Receiver: TypePath + GetOwnership,
$($Arg: TypePath + GetOwnership,)*
for<'a> &'a mut ReturnType: TypePath + GetOwnership,
Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut ReturnType,
{
fn function_info() -> FunctionInfo {
create_info::<Function>()
.with_args({
#[allow(unused_mut)]
let mut _index = 1;
vec![
ArgInfo::new::<&mut Receiver>(0),
$(ArgInfo::new::<$Arg>({
_index += 1;
_index - 1
}),)*
]
})
.with_return_info(ReturnInfo::new::<&mut ReturnType>())
}
}
impl<Receiver, $($Arg,)* ReturnType, Function> TypedFunction<fn(&mut Receiver, $($Arg),*) -> &ReturnType> for Function
where
for<'a> &'a mut Receiver: TypePath + GetOwnership,
$($Arg: TypePath + GetOwnership,)*
for<'a> &'a ReturnType: TypePath + GetOwnership,
Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a ReturnType,
{
fn function_info() -> FunctionInfo {
create_info::<Function>()
.with_args({
#[allow(unused_mut)]
let mut _index = 1;
vec![
ArgInfo::new::<&mut Receiver>(0),
$(ArgInfo::new::<$Arg>({
_index += 1;
_index - 1
}),)*
]
})
.with_return_info(ReturnInfo::new::<&ReturnType>())
}
}
};
}
all_tuples!(impl_typed_function, 0, 15, Arg, arg);
fn create_info<F>() -> FunctionInfo {
let name = core::any::type_name::<F>();
if name.ends_with("{{closure}}") || name.starts_with("fn(") {
FunctionInfo::anonymous()
} else {
FunctionInfo::named(name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_create_function_info() {
fn add(a: i32, b: i32) -> i32 {
a + b
}
assert_eq!(
core::any::type_name_of_val(&add),
"bevy_reflect::func::info::tests::should_create_function_info::add"
);
let info = add.get_function_info();
assert_eq!(
info.name().unwrap(),
"bevy_reflect::func::info::tests::should_create_function_info::add"
);
assert_eq!(info.arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "i32");
}
#[test]
fn should_create_function_pointer_info() {
fn add(a: i32, b: i32) -> i32 {
a + b
}
let add = add as fn(i32, i32) -> i32;
assert_eq!(core::any::type_name_of_val(&add), "fn(i32, i32) -> i32");
let info = add.get_function_info();
assert!(info.name().is_none());
assert_eq!(info.arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "i32");
}
#[test]
fn should_create_anonymous_function_info() {
let add = |a: i32, b: i32| a + b;
assert_eq!(
core::any::type_name_of_val(&add),
"bevy_reflect::func::info::tests::should_create_anonymous_function_info::{{closure}}"
);
let info = add.get_function_info();
assert!(info.name().is_none());
assert_eq!(info.arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "i32");
}
#[test]
fn should_create_closure_info() {
let mut total = 0;
let add = |a: i32, b: i32| total = a + b;
assert_eq!(
core::any::type_name_of_val(&add),
"bevy_reflect::func::info::tests::should_create_closure_info::{{closure}}"
);
let info = add.get_function_info();
assert!(info.name().is_none());
assert_eq!(info.arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "()");
}
}