Improve function info representation

Replaced FunctionInfoType with FunctionInfo and added
SignatureInfo
This commit is contained in:
Gino Valente 2024-12-07 15:00:26 -07:00
parent 0a50c2a0cb
commit a6121d62f9
8 changed files with 623 additions and 633 deletions

View file

@ -2,9 +2,9 @@ use crate::{
self as bevy_reflect, self as bevy_reflect,
__macro_exports::RegisterForReflection, __macro_exports::RegisterForReflection,
func::{ func::{
args::ArgList, dynamic_function_internal::DynamicFunctionInternal, info::FunctionInfoType, args::ArgList, dynamic_function_internal::DynamicFunctionInternal, info::FunctionInfo,
signature::ArgumentSignature, DynamicFunctionMut, Function, FunctionOverloadError, DynamicFunctionMut, Function, FunctionOverloadError, FunctionResult, IntoFunction,
FunctionResult, IntoFunction, IntoFunctionMut, IntoFunctionMut,
}, },
serde::Serializable, serde::Serializable,
ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
@ -83,14 +83,14 @@ impl<'env> DynamicFunction<'env> {
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if no [`FunctionInfo`] is provided or if the conversion to [`FunctionInfoType`] fails. /// Panics if no [`SignatureInfo`] is provided or if the conversion to [`FunctionInfo`] fails.
/// ///
/// [calling]: crate::func::dynamic_function::DynamicFunction::call /// [calling]: crate::func::dynamic_function::DynamicFunction::call
/// [`FunctionInfo`]: crate::func::FunctionInfo /// [`SignatureInfo`]: crate::func::SignatureInfo
/// [function overloading]: Self::with_overload /// [function overloading]: Self::with_overload
pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>( pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>(
func: F, func: F,
info: impl TryInto<FunctionInfoType<'static>, Error: Debug>, info: impl TryInto<FunctionInfo, Error: Debug>,
) -> Self { ) -> Self {
Self { Self {
internal: DynamicFunctionInternal::new(Arc::new(func), info.try_into().unwrap()), internal: DynamicFunctionInternal::new(Arc::new(func), info.try_into().unwrap()),
@ -106,7 +106,7 @@ impl<'env> DynamicFunction<'env> {
/// ///
/// [`DynamicFunctions`]: DynamicFunction /// [`DynamicFunctions`]: DynamicFunction
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self { pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.internal.set_name(Some(name.into())); self.internal = self.internal.with_name(name);
self self
} }
@ -205,7 +205,7 @@ impl<'env> DynamicFunction<'env> {
/// func = func.with_overload(sub); /// func = func.with_overload(sub);
/// ``` /// ```
/// ///
/// [argument signature]: ArgumentSignature /// [argument signature]: crate::func::signature::ArgumentSignature
/// [name]: Self::name /// [name]: Self::name
/// [`try_with_overload`]: Self::try_with_overload /// [`try_with_overload`]: Self::try_with_overload
pub fn with_overload<'a, F: IntoFunction<'a, Marker>, Marker>( pub fn with_overload<'a, F: IntoFunction<'a, Marker>, Marker>(
@ -215,15 +215,9 @@ impl<'env> DynamicFunction<'env> {
where where
'env: 'a, 'env: 'a,
{ {
let function = function.into_function(); self.try_with_overload(function).unwrap_or_else(|(_, err)| {
let internal = self
.internal
.merge(function.internal)
.unwrap_or_else(|(_, err)| {
panic!("{}", err); panic!("{}", err);
}); })
DynamicFunction { internal }
} }
/// Attempt to add an overload to this function. /// Attempt to add an overload to this function.
@ -235,19 +229,14 @@ impl<'env> DynamicFunction<'env> {
/// ///
/// [`with_overload`]: Self::with_overload /// [`with_overload`]: Self::with_overload
pub fn try_with_overload<F: IntoFunction<'env, Marker>, Marker>( pub fn try_with_overload<F: IntoFunction<'env, Marker>, Marker>(
self, mut self,
function: F, function: F,
) -> Result<Self, (Box<Self>, FunctionOverloadError)> { ) -> Result<Self, (Box<Self>, FunctionOverloadError)> {
let function = function.into_function(); let function = function.into_function();
match self.internal.merge(function.internal) { match self.internal.merge(function.internal) {
Ok(internal) => Ok(Self { internal }), Ok(_) => Ok(self),
Err((internal, err)) => Err(( Err(err) => Err((Box::new(self), err)),
Box::new(Self {
internal: *internal,
}),
err,
)),
} }
} }
@ -281,7 +270,7 @@ impl<'env> DynamicFunction<'env> {
} }
/// Returns the function info. /// Returns the function info.
pub fn info(&self) -> FunctionInfoType { pub fn info(&self) -> &FunctionInfo {
self.internal.info() self.internal.info()
} }
@ -350,7 +339,7 @@ impl Function for DynamicFunction<'static> {
self.internal.name() self.internal.name()
} }
fn info(&self) -> FunctionInfoType { fn info(&self) -> &FunctionInfo {
self.internal.info() self.internal.info()
} }
@ -484,10 +473,11 @@ impl<'env> IntoFunctionMut<'env, ()> for DynamicFunction<'env> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::func::{FunctionError, FunctionInfo, IntoReturn}; use crate::func::signature::ArgumentSignature;
use crate::func::{FunctionError, IntoReturn, SignatureInfo};
use crate::Type; use crate::Type;
use bevy_utils::HashSet; use bevy_utils::HashSet;
use std::ops::Add; use core::ops::Add;
#[test] #[test]
fn should_overwrite_function_name() { fn should_overwrite_function_name() {
@ -607,7 +597,7 @@ mod tests {
}, },
// The `FunctionInfo` doesn't really matter for this test // The `FunctionInfo` doesn't really matter for this test
// so we can just give it dummy information. // so we can just give it dummy information.
FunctionInfo::anonymous() SignatureInfo::anonymous()
.with_arg::<i32>("curr") .with_arg::<i32>("curr")
.with_arg::<()>("this"), .with_arg::<()>("this"),
); );
@ -635,18 +625,18 @@ mod tests {
} }
}, },
vec![ vec![
FunctionInfo::named("add::<i32>") SignatureInfo::named("add::<i32>")
.with_arg::<i32>("a") .with_arg::<i32>("a")
.with_arg::<i32>("b") .with_arg::<i32>("b")
.with_return::<i32>(), .with_return::<i32>(),
FunctionInfo::named("add::<f32>") SignatureInfo::named("add::<f32>")
.with_arg::<f32>("a") .with_arg::<f32>("a")
.with_arg::<f32>("b") .with_arg::<f32>("b")
.with_return::<f32>(), .with_return::<f32>(),
], ],
); );
assert!(func.name().is_none()); assert_eq!(func.name().unwrap(), "add::<i32>");
let func = func.with_name("add"); let func = func.with_name("add");
assert_eq!(func.name().unwrap(), "add"); assert_eq!(func.name().unwrap(), "add");
@ -660,9 +650,7 @@ mod tests {
} }
#[test] #[test]
#[should_panic( #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: MissingSignature")]
expected = "called `Result::unwrap()` on an `Err` value: MissingFunctionInfoError"
)]
fn should_panic_on_missing_function_info() { fn should_panic_on_missing_function_info() {
let _ = DynamicFunction::new(|_| Ok(().into_return()), Vec::new()); let _ = DynamicFunction::new(|_| Ok(().into_return()), Vec::new());
} }
@ -726,11 +714,11 @@ mod tests {
} }
}, },
vec![ vec![
FunctionInfo::named("add::<i32>") SignatureInfo::named("add::<i32>")
.with_arg::<i32>("a") .with_arg::<i32>("a")
.with_arg::<i32>("b") .with_arg::<i32>("b")
.with_return::<i32>(), .with_return::<i32>(),
FunctionInfo::named("add::<f32>") SignatureInfo::named("add::<f32>")
.with_arg::<f32>("a") .with_arg::<f32>("a")
.with_arg::<f32>("b") .with_arg::<f32>("b")
.with_return::<f32>(), .with_return::<f32>(),

View file

@ -1,8 +1,5 @@
use crate::func::signature::ArgumentSignature; use crate::func::signature::ArgumentSignature;
use crate::func::{ use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionOverloadError};
ArgList, FunctionError, FunctionInfo, FunctionInfoType, FunctionOverloadError,
PrettyPrintFunctionInfo,
};
use alloc::borrow::Cow; use alloc::borrow::Cow;
use bevy_utils::hashbrown::HashMap; use bevy_utils::hashbrown::HashMap;
use core::fmt::{Debug, Formatter}; use core::fmt::{Debug, Formatter};
@ -18,45 +15,40 @@ use core::ops::RangeInclusive;
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut /// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
#[derive(Clone)] #[derive(Clone)]
pub(super) struct DynamicFunctionInternal<F> { pub(super) struct DynamicFunctionInternal<F> {
name: Option<Cow<'static, str>>, functions: Vec<F>,
map: FunctionMap<F>, info: FunctionInfo,
arg_map: HashMap<ArgumentSignature, usize>,
} }
impl<F> DynamicFunctionInternal<F> { impl<F> DynamicFunctionInternal<F> {
/// Create a new instance of [`DynamicFunctionInternal`] with the given function /// Create a new instance of [`DynamicFunctionInternal`] with the given function
/// and its corresponding information. /// and its corresponding information.
pub fn new(func: F, info: FunctionInfoType<'static>) -> Self { pub fn new(func: F, info: FunctionInfo) -> Self {
Self { let arg_map = info
name: match &info { .signatures()
FunctionInfoType::Standard(info) => info.name().cloned(),
FunctionInfoType::Overloaded(_) => None,
},
map: match info {
FunctionInfoType::Standard(info) => FunctionMap::Single(func, info.into_owned()),
FunctionInfoType::Overloaded(infos) => {
let indices = infos
.iter() .iter()
.map(|info| (ArgumentSignature::from(info), 0)) .map(|sig| (ArgumentSignature::from(sig), 0))
.collect(); .collect();
FunctionMap::Overloaded(vec![func], infos.into_owned(), indices)
}
},
}
}
/// Sets the name of the function. Self {
pub fn set_name(&mut self, name: Option<Cow<'static, str>>) { functions: vec![func],
self.name = name; info,
arg_map,
}
}
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.info = self.info.with_name(Some(name.into()));
self
} }
/// The name of the function. /// The name of the function.
pub fn name(&self) -> Option<&Cow<'static, str>> { pub fn name(&self) -> Option<&Cow<'static, str>> {
self.name.as_ref() self.info.name()
} }
/// Returns `true` if the function is overloaded. /// Returns `true` if the function is overloaded.
pub fn is_overloaded(&self) -> bool { pub fn is_overloaded(&self) -> bool {
matches!(self.map, FunctionMap::Overloaded(..)) self.info.is_overloaded()
} }
/// Get an immutable reference to the function. /// Get an immutable reference to the function.
@ -66,52 +58,45 @@ impl<F> DynamicFunctionInternal<F> {
/// ///
/// If no overload matches the provided arguments, returns [`FunctionError::NoOverload`]. /// If no overload matches the provided arguments, returns [`FunctionError::NoOverload`].
pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> { pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> {
match &self.map { if !self.info.is_overloaded() {
FunctionMap::Single(function, _) => Ok(function), return Ok(&self.functions[0]);
FunctionMap::Overloaded(functions, _, indices) => { }
let signature = ArgumentSignature::from(args); let signature = ArgumentSignature::from(args);
indices self.arg_map
.get(&signature) .get(&signature)
.map(|index| &functions[*index]) .map(|index| &self.functions[*index])
.ok_or_else(|| FunctionError::NoOverload { .ok_or_else(|| FunctionError::NoOverload {
expected: indices.keys().cloned().collect(), expected: self.arg_map.keys().cloned().collect(),
received: signature, received: signature,
}) })
} }
}
}
/// Get an mutable reference to the function. /// Get a mutable reference to the function.
/// ///
/// If the function is not overloaded, it will always be returned regardless of the arguments. /// If the function is not overloaded, it will always be returned regardless of the arguments.
/// Otherwise, the function will be selected based on the arguments provided. /// Otherwise, the function will be selected based on the arguments provided.
/// ///
/// If no overload matches the provided arguments, returns [`FunctionError::NoOverload`]. /// If no overload matches the provided arguments, returns [`FunctionError::NoOverload`].
pub fn get_mut(&mut self, args: &ArgList) -> Result<&mut F, FunctionError> { pub fn get_mut(&mut self, args: &ArgList) -> Result<&mut F, FunctionError> {
match &mut self.map { if !self.info.is_overloaded() {
FunctionMap::Single(function, _) => Ok(function), return Ok(&mut self.functions[0]);
FunctionMap::Overloaded(functions, _, indices) => { }
let signature = ArgumentSignature::from(args); let signature = ArgumentSignature::from(args);
indices self.arg_map
.get(&signature) .get(&signature)
.map(|index| &mut functions[*index]) .map(|index| &mut self.functions[*index])
.ok_or_else(|| FunctionError::NoOverload { .ok_or_else(|| FunctionError::NoOverload {
expected: indices.keys().cloned().collect(), expected: self.arg_map.keys().cloned().collect(),
received: signature, received: signature,
}) })
} }
}
}
/// Returns the function information contained in the map. /// Returns the function information contained in the map.
#[inline] #[inline]
pub fn info(&self) -> FunctionInfoType { pub fn info(&self) -> &FunctionInfo {
match &self.map { &self.info
FunctionMap::Single(_, info) => FunctionInfoType::Standard(Cow::Borrowed(info)),
FunctionMap::Overloaded(_, info, _) => {
FunctionInfoType::Overloaded(Cow::Borrowed(info))
}
}
} }
/// Returns the number of arguments the function expects. /// Returns the number of arguments the function expects.
@ -121,7 +106,7 @@ impl<F> DynamicFunctionInternal<F> {
/// ///
/// Otherwise, the range will have the same start and end. /// Otherwise, the range will have the same start and end.
pub fn arg_count(&self) -> RangeInclusive<usize> { pub fn arg_count(&self) -> RangeInclusive<usize> {
self.info().arg_count() self.info.arg_count()
} }
/// Helper method for validating that a given set of arguments are _potentially_ valid for this function. /// Helper method for validating that a given set of arguments are _potentially_ valid for this function.
@ -155,215 +140,74 @@ impl<F> DynamicFunctionInternal<F> {
/// `[func_a, func_b, func_c, func_d]`. /// `[func_a, func_b, func_c, func_d]`.
/// And merging `[func_c, func_d]` (self) with `[func_a, func_b]` (other) should result in /// And merging `[func_c, func_d]` (self) with `[func_a, func_b]` (other) should result in
/// `[func_c, func_d, func_a, func_b]`. /// `[func_c, func_d, func_a, func_b]`.
pub fn merge(self, other: Self) -> Result<Self, (Box<Self>, FunctionOverloadError)> { pub fn merge(&mut self, mut other: Self) -> Result<(), FunctionOverloadError> {
let map = self.map.merge(other.map).map_err(|(map, err)| { // Keep a separate map of the new indices to avoid mutating the existing one
( // until we can be sure the merge will be successful.
Box::new(Self { let mut new_signatures = HashMap::new();
name: self.name.clone(),
map: *map,
}),
err,
)
})?;
Ok(Self { for (sig, index) in other.arg_map {
name: self.name, if self.arg_map.contains_key(&sig) {
map, return Err(FunctionOverloadError::DuplicateSignature(sig));
})
} }
/// Convert the inner [`FunctionMap`] from holding `F` to holding `G`. new_signatures.insert_unique_unchecked(sig, self.functions.len() + index);
pub fn convert<G>(self, f: fn(FunctionMap<F>) -> FunctionMap<G>) -> DynamicFunctionInternal<G> { }
self.arg_map.reserve(new_signatures.len());
for (sig, index) in new_signatures {
self.arg_map.insert_unique_unchecked(sig, index);
}
self.functions.append(&mut other.functions);
self.info.extend_unchecked(other.info);
Ok(())
}
/// Maps the internally stored function(s) from type `F` to type `G`.
pub fn map_functions<G>(self, f: fn(F) -> G) -> DynamicFunctionInternal<G> {
DynamicFunctionInternal { DynamicFunctionInternal {
name: self.name, functions: self.functions.into_iter().map(f).collect(),
map: f(self.map), info: self.info,
arg_map: self.arg_map,
} }
} }
} }
impl<F> Debug for DynamicFunctionInternal<F> { impl<F> Debug for DynamicFunctionInternal<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let name = self.name.as_deref().unwrap_or("_"); self.info
write!(f, "fn {name}")?; .pretty_printer()
.include_fn_token()
match &self.map { .include_name()
// `(arg0: i32, arg1: i32) -> ()` .fmt(f)
FunctionMap::Single(_, info) => PrettyPrintFunctionInfo::new(info).fmt(f),
// `{(arg0: i32, arg1: i32) -> (), (arg0: f32, arg1: f32) -> ()}`
FunctionMap::Overloaded(_, infos, _) => {
let mut set = f.debug_set();
for info in infos.iter() {
set.entry(&PrettyPrintFunctionInfo::new(info));
}
set.finish()
}
}
}
}
/// A helper type for storing a mapping of overloaded functions
/// along with the corresponding [function information].
///
/// By using an enum, we can optimize the common case of a single function,
/// while still allowing for multiple function overloads to be stored.
///
/// [function information]: FunctionInfo
#[derive(Clone, Debug)]
pub(super) enum FunctionMap<F> {
/// Represents a single, non-overloaded function.
Single(F, FunctionInfo),
/// Represents an overloaded function.
Overloaded(
/// The list of function overloads.
Vec<F>,
/// The information for each function.
///
/// Note that some functions may have multiple `FunctionInfo` values (i.e. manually created overloads),
/// so this list may not always line up one-to-one with the functions list.
Vec<FunctionInfo>,
/// A mapping of argument signatures to the index of the corresponding function.
///
/// Multiple signatures may point to the same function index (i.e. for manually created overloads).
HashMap<ArgumentSignature, usize>,
),
}
impl<F> FunctionMap<F> {
/// Merge another [`FunctionMap`] into this one.
///
/// If the other map contains any functions with the same signature as this one,
/// an error will be returned along with the original, unchanged map.
///
/// Therefore, this method should always return `Ok(Self::Overloaded(..))` if the merge is successful.
///
/// Additionally, if the merge succeeds, it should be guaranteed that the order
/// of the functions in the map will be preserved.
/// For example, merging `[func_a, func_b]` (self) with `[func_c, func_d]` (other) should result in
/// `[func_a, func_b, func_c, func_d]`.
/// And merging `[func_c, func_d]` (self) with `[func_a, func_b]` (other) should result in
/// `[func_c, func_d, func_a, func_b]`.
pub fn merge(self, other: Self) -> Result<Self, (Box<Self>, FunctionOverloadError)> {
match (self, other) {
(Self::Single(self_func, self_info), Self::Single(other_func, other_info)) => {
let self_sig = ArgumentSignature::from(&self_info);
let other_sig = ArgumentSignature::from(&other_info);
if self_sig == other_sig {
return Err((
Box::new(Self::Single(self_func, self_info)),
FunctionOverloadError {
signature: self_sig,
},
));
}
let mut map = HashMap::new();
map.insert_unique_unchecked(self_sig, 0);
map.insert_unique_unchecked(other_sig, 1);
Ok(Self::Overloaded(
vec![self_func, other_func],
vec![self_info, other_info],
map,
))
}
(
Self::Single(self_func, self_info),
Self::Overloaded(mut other_funcs, mut other_infos, mut other_indices),
) => {
let self_sig = ArgumentSignature::from(&self_info);
if other_indices.contains_key(&self_sig) {
return Err((
Box::new(Self::Single(self_func, self_info)),
FunctionOverloadError {
signature: self_sig,
},
));
}
for index in other_indices.values_mut() {
*index += 1;
}
other_funcs.insert(0, self_func);
other_infos.insert(0, self_info);
other_indices.insert_unique_unchecked(self_sig, 0);
Ok(Self::Overloaded(other_funcs, other_infos, other_indices))
}
(
Self::Overloaded(mut self_funcs, mut self_infos, mut self_indices),
Self::Single(other_func, other_info),
) => {
let other_sig = ArgumentSignature::from(&other_info);
if self_indices.contains_key(&other_sig) {
return Err((
Box::new(Self::Overloaded(self_funcs, self_infos, self_indices)),
FunctionOverloadError {
signature: other_sig,
},
));
}
let index = self_funcs.len();
self_funcs.push(other_func);
self_infos.push(other_info);
self_indices.insert_unique_unchecked(other_sig, index);
Ok(Self::Overloaded(self_funcs, self_infos, self_indices))
}
(
Self::Overloaded(mut self_funcs, mut self_infos, mut self_indices),
Self::Overloaded(mut other_funcs, mut other_infos, other_indices),
) => {
// Keep a separate map of the new indices to avoid mutating the existing one
// until we can be sure the merge will be successful.
let mut new_indices = HashMap::new();
for (sig, index) in other_indices {
if self_indices.contains_key(&sig) {
return Err((
Box::new(Self::Overloaded(self_funcs, self_infos, self_indices)),
FunctionOverloadError { signature: sig },
));
}
new_indices.insert_unique_unchecked(sig, self_funcs.len() + index);
}
self_indices.reserve(new_indices.len());
for (sig, index) in new_indices {
self_indices.insert_unique_unchecked(sig, index);
}
self_funcs.append(&mut other_funcs);
// The index map and `FunctionInfo` list should always be in sync,
// so we can simply append the new infos to the existing ones.
self_infos.append(&mut other_infos);
Ok(Self::Overloaded(self_funcs, self_infos, self_indices))
}
}
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::func::FunctionInfo; use crate::func::{FunctionInfo, SignatureInfo};
use crate::Type; use crate::Type;
#[test] #[test]
fn should_merge_single_into_single() { fn should_merge_single_into_single() {
let map_a = FunctionMap::Single('a', FunctionInfo::anonymous().with_arg::<i8>("arg0")); let mut func_a = DynamicFunctionInternal::new(
let map_b = FunctionMap::Single('b', FunctionInfo::anonymous().with_arg::<u8>("arg0")); 'a',
FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0")),
);
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else { let func_b = DynamicFunctionInternal::new(
panic!("expected overloaded function"); 'b',
}; FunctionInfo::new(SignatureInfo::anonymous().with_arg::<u8>("arg0")),
assert_eq!(functions, vec!['a', 'b']); );
assert_eq!(infos.len(), 2);
func_a.merge(func_b).unwrap();
assert_eq!(func_a.functions, vec!['a', 'b']);
assert_eq!(func_a.info.signatures().len(), 2);
assert_eq!( assert_eq!(
indices, func_a.arg_map,
HashMap::from_iter([ HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), (ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 1), (ArgumentSignature::from_iter([Type::of::<u8>()]), 1),
@ -373,26 +217,28 @@ mod tests {
#[test] #[test]
fn should_merge_single_into_overloaded() { fn should_merge_single_into_overloaded() {
let map_a = FunctionMap::Single('a', FunctionInfo::anonymous().with_arg::<i8>("arg0")); let mut func_a = DynamicFunctionInternal::new(
let map_b = FunctionMap::Overloaded( 'a',
vec!['b', 'c'], FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0")),
vec![ );
FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<u16>("arg0"), let func_b = DynamicFunctionInternal {
], functions: vec!['b', 'c'],
HashMap::from_iter([ info: FunctionInfo::new(SignatureInfo::anonymous().with_arg::<u8>("arg0"))
.with_overload(SignatureInfo::anonymous().with_arg::<u16>("arg0"))
.unwrap(),
arg_map: HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0), (ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 1), (ArgumentSignature::from_iter([Type::of::<u16>()]), 1),
]), ]),
);
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
}; };
assert_eq!(functions, vec!['a', 'b', 'c']);
assert_eq!(infos.len(), 3); func_a.merge(func_b).unwrap();
assert_eq!(func_a.functions, vec!['a', 'b', 'c']);
assert_eq!(func_a.info.signatures().len(), 3);
assert_eq!( assert_eq!(
indices, func_a.arg_map,
HashMap::from_iter([ HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), (ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 1), (ArgumentSignature::from_iter([Type::of::<u8>()]), 1),
@ -403,26 +249,28 @@ mod tests {
#[test] #[test]
fn should_merge_overloaed_into_single() { fn should_merge_overloaed_into_single() {
let map_a = FunctionMap::Overloaded( let mut func_a = DynamicFunctionInternal {
vec!['a', 'b'], functions: vec!['a', 'b'],
vec![ info: FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0"))
FunctionInfo::anonymous().with_arg::<i8>("arg0"), .with_overload(SignatureInfo::anonymous().with_arg::<i16>("arg0"))
FunctionInfo::anonymous().with_arg::<i16>("arg0"), .unwrap(),
], arg_map: HashMap::from_iter([
HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), (ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1), (ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
]), ]),
);
let map_b = FunctionMap::Single('c', FunctionInfo::anonymous().with_arg::<u8>("arg0"));
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
}; };
assert_eq!(functions, vec!['a', 'b', 'c']);
assert_eq!(infos.len(), 3); let func_b = DynamicFunctionInternal::new(
'c',
FunctionInfo::new(SignatureInfo::anonymous().with_arg::<u8>("arg0")),
);
func_a.merge(func_b).unwrap();
assert_eq!(func_a.functions, vec!['a', 'b', 'c']);
assert_eq!(func_a.info.signatures().len(), 3);
assert_eq!( assert_eq!(
indices, func_a.arg_map,
HashMap::from_iter([ HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), (ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1), (ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
@ -433,36 +281,34 @@ mod tests {
#[test] #[test]
fn should_merge_overloaded_into_overloaded() { fn should_merge_overloaded_into_overloaded() {
let map_a = FunctionMap::Overloaded( let mut func_a = DynamicFunctionInternal {
vec!['a', 'b'], functions: vec!['a', 'b'],
vec![ info: FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0"))
FunctionInfo::anonymous().with_arg::<i8>("arg0"), .with_overload(SignatureInfo::anonymous().with_arg::<i16>("arg0"))
FunctionInfo::anonymous().with_arg::<i16>("arg0"), .unwrap(),
], arg_map: HashMap::from_iter([
HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), (ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1), (ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
]), ]),
); };
let map_b = FunctionMap::Overloaded(
vec!['c', 'd'], let func_b = DynamicFunctionInternal {
vec![ functions: vec!['c', 'd'],
FunctionInfo::anonymous().with_arg::<u8>("arg0"), info: FunctionInfo::new(SignatureInfo::anonymous().with_arg::<u8>("arg0"))
FunctionInfo::anonymous().with_arg::<u16>("arg0"), .with_overload(SignatureInfo::anonymous().with_arg::<u16>("arg0"))
], .unwrap(),
HashMap::from_iter([ arg_map: HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0), (ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 1), (ArgumentSignature::from_iter([Type::of::<u16>()]), 1),
]), ]),
);
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
}; };
assert_eq!(functions, vec!['a', 'b', 'c', 'd']);
assert_eq!(infos.len(), 4); func_a.merge(func_b).unwrap();
assert_eq!(func_a.functions, vec!['a', 'b', 'c', 'd']);
assert_eq!(func_a.info.signatures().len(), 4);
assert_eq!( assert_eq!(
indices, func_a.arg_map,
HashMap::from_iter([ HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), (ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1), (ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
@ -474,19 +320,29 @@ mod tests {
#[test] #[test]
fn should_return_error_on_duplicate_signature() { fn should_return_error_on_duplicate_signature() {
let map_a = FunctionMap::Single( let mut func_a = DynamicFunctionInternal::new(
'a', 'a',
FunctionInfo::anonymous() FunctionInfo::new(
SignatureInfo::anonymous()
.with_arg::<i8>("arg0") .with_arg::<i8>("arg0")
.with_arg::<i16>("arg1"), .with_arg::<i16>("arg1"),
),
); );
let map_b = FunctionMap::Overloaded(
vec!['b', 'c'], let func_b = DynamicFunctionInternal {
vec![ functions: vec!['b', 'c'],
FunctionInfo::anonymous().with_arg::<u8>("arg0"), info: FunctionInfo::new(
FunctionInfo::anonymous().with_arg::<u16>("arg1"), SignatureInfo::anonymous()
], .with_arg::<u8>("arg0")
HashMap::from_iter([ .with_arg::<u16>("arg1"),
)
.with_overload(
SignatureInfo::anonymous()
.with_arg::<i8>("arg0")
.with_arg::<i16>("arg1"),
)
.unwrap(),
arg_map: HashMap::from_iter([
( (
ArgumentSignature::from_iter([Type::of::<u8>(), Type::of::<u16>()]), ArgumentSignature::from_iter([Type::of::<u8>(), Type::of::<u16>()]),
0, 0,
@ -496,23 +352,29 @@ mod tests {
1, 1,
), ),
]), ]),
);
let (map, error) = map_a.merge(map_b).unwrap_err();
assert_eq!(
error.signature,
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
);
// Assert the original map remains unchanged:
let FunctionMap::Single(function, info) = *map else {
panic!("expected single function");
}; };
assert_eq!(function, 'a'); let FunctionOverloadError::DuplicateSignature(duplicate) =
func_a.merge(func_b).unwrap_err()
else {
panic!("Expected `FunctionOverloadError::DuplicateSignature`");
};
assert_eq!( assert_eq!(
ArgumentSignature::from(info), duplicate,
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()]) ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
); );
// Assert the original remains unchanged:
assert!(!func_a.is_overloaded());
assert_eq!(func_a.functions, vec!['a']);
assert_eq!(func_a.info.signatures().len(), 1);
assert_eq!(
func_a.arg_map,
HashMap::from_iter([(
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()]),
0
),])
);
} }
} }

View file

@ -5,10 +5,9 @@ use core::ops::RangeInclusive;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::{boxed::Box, format, vec}; use alloc::{boxed::Box, format, vec};
use crate::func::dynamic_function_internal::FunctionMap;
use crate::func::{ use crate::func::{
args::ArgList, dynamic_function_internal::DynamicFunctionInternal, DynamicFunction, args::ArgList, dynamic_function_internal::DynamicFunctionInternal, DynamicFunction,
FunctionError, FunctionInfoType, FunctionOverloadError, FunctionResult, IntoFunctionMut, FunctionInfo, FunctionOverloadError, FunctionResult, IntoFunctionMut,
}; };
/// A [`Box`] containing a callback to a reflected function. /// A [`Box`] containing a callback to a reflected function.
@ -86,14 +85,14 @@ impl<'env> DynamicFunctionMut<'env> {
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if no [`FunctionInfo`] is provided or if the conversion to [`FunctionInfoType`] fails. /// Panics if no [`SignatureInfo`] is provided or if the conversion to [`FunctionInfo`] fails.
/// ///
/// [calling]: crate::func::dynamic_function_mut::DynamicFunctionMut::call /// [calling]: crate::func::dynamic_function_mut::DynamicFunctionMut::call
/// [`FunctionInfo`]: crate::func::FunctionInfo /// [`SignatureInfo`]: crate::func::SignatureInfo
/// [function overloading]: Self::with_overload /// [function overloading]: Self::with_overload
pub fn new<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>( pub fn new<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>(
func: F, func: F,
info: impl TryInto<FunctionInfoType<'static>, Error: Debug>, info: impl TryInto<FunctionInfo, Error: Debug>,
) -> Self { ) -> Self {
Self { Self {
internal: DynamicFunctionInternal::new(Box::new(func), info.try_into().unwrap()), internal: DynamicFunctionInternal::new(Box::new(func), info.try_into().unwrap()),
@ -109,7 +108,7 @@ impl<'env> DynamicFunctionMut<'env> {
/// ///
/// [`DynamicFunctionMuts`]: DynamicFunctionMut /// [`DynamicFunctionMuts`]: DynamicFunctionMut
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self { pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.internal.set_name(Some(name.into())); self.internal = self.internal.with_name(name);
self self
} }
@ -167,7 +166,7 @@ impl<'env> DynamicFunctionMut<'env> {
/// assert_eq!(total_f32, 1.23); /// assert_eq!(total_f32, 1.23);
/// ``` /// ```
/// ///
/// [argument signature]: ArgumentSignature /// [argument signature]: crate::func::signature::ArgumentSignature
/// [name]: Self::name /// [name]: Self::name
/// [`try_with_overload`]: Self::try_with_overload /// [`try_with_overload`]: Self::try_with_overload
pub fn with_overload<'a, F: IntoFunctionMut<'a, Marker>, Marker>( pub fn with_overload<'a, F: IntoFunctionMut<'a, Marker>, Marker>(
@ -177,16 +176,9 @@ impl<'env> DynamicFunctionMut<'env> {
where where
'env: 'a, 'env: 'a,
{ {
let function = function.into_function_mut(); self.try_with_overload(function).unwrap_or_else(|(_, err)| {
let internal = self
.internal
.merge(function.internal)
.unwrap_or_else(|(_, err)| {
panic!("{}", err); panic!("{}", err);
}); })
DynamicFunctionMut { internal }
} }
/// Attempt to add an overload to this function. /// Attempt to add an overload to this function.
@ -198,19 +190,14 @@ impl<'env> DynamicFunctionMut<'env> {
/// ///
/// [`with_overload`]: Self::with_overload /// [`with_overload`]: Self::with_overload
pub fn try_with_overload<F: IntoFunctionMut<'env, Marker>, Marker>( pub fn try_with_overload<F: IntoFunctionMut<'env, Marker>, Marker>(
self, mut self,
function: F, function: F,
) -> Result<Self, (Box<Self>, FunctionOverloadError)> { ) -> Result<Self, (Box<Self>, FunctionOverloadError)> {
let function = function.into_function_mut(); let function = function.into_function_mut();
match self.internal.merge(function.internal) { match self.internal.merge(function.internal) {
Ok(internal) => Ok(Self { internal }), Ok(_) => Ok(self),
Err((internal, err)) => Err(( Err(err) => Err((Box::new(self), err)),
Box::new(Self {
internal: *internal,
}),
err,
)),
} }
} }
@ -284,7 +271,7 @@ impl<'env> DynamicFunctionMut<'env> {
} }
/// Returns the function info. /// Returns the function info.
pub fn info(&self) -> FunctionInfoType { pub fn info(&self) -> &FunctionInfo {
self.internal.info() self.internal.info()
} }
@ -367,14 +354,7 @@ impl<'env> From<DynamicFunction<'env>> for DynamicFunctionMut<'env> {
#[inline] #[inline]
fn from(function: DynamicFunction<'env>) -> Self { fn from(function: DynamicFunction<'env>) -> Self {
Self { Self {
internal: function.internal.convert(|map| match map { internal: function.internal.map_functions(arc_to_box),
FunctionMap::Single(func, info) => FunctionMap::Single(arc_to_box(func), info),
FunctionMap::Overloaded(funcs, infos, indices) => FunctionMap::Overloaded(
funcs.into_iter().map(arc_to_box).collect(),
infos,
indices,
),
}),
} }
} }
} }
@ -398,8 +378,8 @@ impl<'env> IntoFunctionMut<'env, ()> for DynamicFunctionMut<'env> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::func::{FunctionInfo, IntoReturn}; use crate::func::{FunctionError, IntoReturn, SignatureInfo};
use std::ops::Add; use core::ops::Add;
#[test] #[test]
fn should_overwrite_function_name() { fn should_overwrite_function_name() {
@ -466,12 +446,12 @@ mod tests {
Ok(().into_return()) Ok(().into_return())
}, },
vec![ vec![
FunctionInfo::named("add::<i32>").with_arg::<i32>("value"), SignatureInfo::named("add::<i32>").with_arg::<i32>("value"),
FunctionInfo::named("add::<i16>").with_arg::<i16>("value"), SignatureInfo::named("add::<i16>").with_arg::<i16>("value"),
], ],
); );
assert!(func.name().is_none()); assert_eq!(func.name().unwrap(), "add::<i32>");
let mut func = func.with_name("add"); let mut func = func.with_name("add");
assert_eq!(func.name().unwrap(), "add"); assert_eq!(func.name().unwrap(), "add");

View file

@ -40,18 +40,17 @@ pub enum FunctionError {
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut /// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
pub type FunctionResult<'a> = Result<Return<'a>, FunctionError>; pub type FunctionResult<'a> = Result<Return<'a>, FunctionError>;
/// A [`FunctionInfo`] was expected but none was found. /// An error that occurs when attempting to add a function overload.
///
/// [`FunctionInfo`]: crate::func::FunctionInfo
#[derive(Debug, Error, PartialEq)] #[derive(Debug, Error, PartialEq)]
#[error("expected a `FunctionInfo` but found none")] pub enum FunctionOverloadError {
pub struct MissingFunctionInfoError; /// A [`SignatureInfo`] was expected, but none was found.
///
/// An error that occurs when attempting to add a function overload with a duplicate signature. /// [`SignatureInfo`]: crate::func::info::SignatureInfo
#[derive(Debug, Error, PartialEq)] #[error("expected at least one `SignatureInfo` but found none")]
#[error("could not add function overload: duplicate found for signature `{signature:?}`")] MissingSignature,
pub struct FunctionOverloadError { /// An error that occurs when attempting to add a function overload with a duplicate signature.
pub signature: ArgumentSignature, #[error("could not add function overload: duplicate found for signature `{0:?}`")]
DuplicateSignature(ArgumentSignature),
} }
/// An error that occurs when registering a function into a [`FunctionRegistry`]. /// An error that occurs when registering a function into a [`FunctionRegistry`].

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
func::{ArgList, DynamicFunction, FunctionInfoType, FunctionResult}, func::{ArgList, DynamicFunction, FunctionInfo, FunctionResult},
PartialReflect, PartialReflect,
}; };
use alloc::borrow::Cow; use alloc::borrow::Cow;
@ -50,18 +50,16 @@ pub trait Function: PartialReflect + Debug {
/// Returns the number of arguments the function expects. /// Returns the number of arguments the function expects.
/// ///
/// For [overloaded] functions that can have a variable number of arguments, /// For overloaded functions that can have a variable number of arguments,
/// this will return the minimum and maximum number of arguments. /// this will return the minimum and maximum number of arguments.
/// ///
/// Otherwise, the range will have the same start and end. /// Otherwise, the range will have the same start and end.
///
/// [overloaded]: FunctionInfoType::Overloaded
fn arg_count(&self) -> RangeInclusive<usize> { fn arg_count(&self) -> RangeInclusive<usize> {
self.info().arg_count() self.info().arg_count()
} }
/// The [`FunctionInfoType`] for this function. /// The [`FunctionInfo`] for this function.
fn info(&self) -> FunctionInfoType; fn info(&self) -> &FunctionInfo;
/// Call this function with the given arguments. /// Call this function with the given arguments.
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>; fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>;

View file

@ -3,134 +3,232 @@ use alloc::{borrow::Cow, vec};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::{boxed::Box, format, vec}; use alloc::{boxed::Box, format, vec};
use core::fmt::{Debug, Formatter};
use core::ops::RangeInclusive;
use variadics_please::all_tuples;
use crate::{ use crate::{
func::{ func::args::{ArgInfo, GetOwnership, Ownership},
args::{ArgInfo, GetOwnership, Ownership}, func::signature::ArgumentSignature,
MissingFunctionInfoError, func::FunctionOverloadError,
},
type_info::impl_type_methods, type_info::impl_type_methods,
Type, TypePath, Type, TypePath,
}; };
/// A wrapper around [`FunctionInfo`] used to represent either a standard function use core::fmt::{Debug, Formatter};
/// or an overloaded function. use core::ops::RangeInclusive;
#[derive(Debug, Clone)] use variadics_please::all_tuples;
pub enum FunctionInfoType<'a> {
/// A standard function with a single set of arguments.
///
/// This includes generic functions with a single set of monomorphized arguments.
Standard(Cow<'a, FunctionInfo>),
/// An overloaded function with multiple sets of arguments.
///
/// This includes generic functions with multiple sets of monomorphized arguments,
/// as well as functions with a variable number of arguments (i.e. "variadic functions").
Overloaded(Cow<'a, [FunctionInfo]>),
}
impl From<FunctionInfo> for FunctionInfoType<'_> {
fn from(info: FunctionInfo) -> Self {
FunctionInfoType::Standard(Cow::Owned(info))
}
}
impl TryFrom<Vec<FunctionInfo>> for FunctionInfoType<'_> {
type Error = MissingFunctionInfoError;
fn try_from(mut infos: Vec<FunctionInfo>) -> Result<Self, Self::Error> {
match infos.len() {
0 => Err(MissingFunctionInfoError),
1 => Ok(Self::Standard(Cow::Owned(infos.pop().unwrap()))),
_ => Ok(Self::Overloaded(Cow::Owned(infos))),
}
}
}
impl IntoIterator for FunctionInfoType<'_> {
type Item = FunctionInfo;
type IntoIter = vec::IntoIter<FunctionInfo>;
fn into_iter(self) -> Self::IntoIter {
// Allow `.into_owned()` so that we can create a `std::vec::IntoIter`
#[allow(clippy::unnecessary_to_owned)]
match self {
FunctionInfoType::Standard(info) => vec![info.into_owned()].into_iter(),
FunctionInfoType::Overloaded(infos) => infos.into_owned().into_iter(),
}
}
}
impl FunctionInfoType<'_> {
/// Returns the number of arguments the function expects.
///
/// For [overloaded] functions that can have a variable number of arguments,
/// this will return the minimum and maximum number of arguments.
///
/// Otherwise, the range will have the same start and end.
///
/// [overloaded]: Self::Overloaded
pub fn arg_count(&self) -> RangeInclusive<usize> {
match self {
Self::Standard(info) => RangeInclusive::new(info.arg_count(), info.arg_count()),
Self::Overloaded(infos) => infos.iter().map(FunctionInfo::arg_count).fold(
RangeInclusive::new(usize::MAX, usize::MIN),
|acc, count| {
RangeInclusive::new((*acc.start()).min(count), (*acc.end()).max(count))
},
),
}
}
}
/// Type information for a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// Type information for a [`DynamicFunction`] or [`DynamicFunctionMut`].
/// ///
/// This information can be retrieved directly from certain functions and closures /// This information can be retrieved directly from certain functions and closures
/// using the [`TypedFunction`] trait, and manually constructed otherwise. /// using the [`TypedFunction`] trait, and manually constructed otherwise.
/// ///
/// It is compromised of one or more [`SignatureInfo`] structs,
/// allowing it to represent functions with multiple sets of arguments (i.e. "overloaded functions").
///
/// [`DynamicFunction`]: crate::func::DynamicFunction /// [`DynamicFunction`]: crate::func::DynamicFunction
/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut /// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FunctionInfo { pub struct FunctionInfo {
name: Option<Cow<'static, str>>, name: Option<Cow<'static, str>>,
args: Vec<ArgInfo>, signatures: Box<[SignatureInfo]>,
return_info: ReturnInfo,
} }
impl FunctionInfo { impl FunctionInfo {
/// Create a new [`FunctionInfo`] for a function with the given name. /// Create a new [`FunctionInfo`] for a function with the given signature.
pub fn new(signature: SignatureInfo) -> Self {
Self {
name: signature.name.clone(),
signatures: vec![signature].into(),
}
}
/// Create a new [`FunctionInfo`] from a set of signatures.
///
/// Returns an error if the given iterator is empty or contains duplicate signatures.
pub fn try_from_iter(
signatures: impl IntoIterator<Item = SignatureInfo>,
) -> Result<Self, FunctionOverloadError> {
let mut iter = signatures.into_iter();
let mut info = Self::new(iter.next().ok_or(FunctionOverloadError::MissingSignature)?);
for signature in iter {
info = info.with_overload(signature).map_err(|sig| {
FunctionOverloadError::DuplicateSignature(ArgumentSignature::from(&sig))
})?;
}
Ok(info)
}
/// The base signature for this function.
///
/// All functions—including overloaded functions—are guaranteed to have at least one signature.
/// The first signature used to define the [`FunctionInfo`] is considered the base signature.
pub fn base(&self) -> &SignatureInfo {
&self.signatures[0]
}
/// Whether this function is overloaded.
///
/// This is determined by the existence of multiple signatures.
pub fn is_overloaded(&self) -> bool {
self.signatures.len() > 1
}
/// Set the name of the function.
pub fn with_name(mut self, name: Option<impl Into<Cow<'static, str>>>) -> Self {
self.name = name.map(Into::into);
self
}
/// The name of the function.
///
/// 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`.
///
/// For overloaded functions, this will be the name of the base signature,
/// unless manually overwritten using [`Self::with_name`].
///
/// [`DynamicFunctions`]: crate::func::DynamicFunction
/// [`IntoFunction`]: crate::func::IntoFunction
/// [`DynamicFunctionMuts`]: crate::func::DynamicFunctionMut
/// [`IntoFunctionMut`]: crate::func::IntoFunctionMut
pub fn name(&self) -> Option<&Cow<'static, str>> {
self.name.as_ref()
}
/// Add a signature to this function.
///
/// If a signature with the same [`ArgumentSignature`] already exists,
/// an error is returned with the given signature.
pub fn with_overload(mut self, signature: SignatureInfo) -> Result<Self, SignatureInfo> {
let is_duplicate = self.signatures.iter().any(|s| {
s.arg_count() == signature.arg_count()
&& ArgumentSignature::from(s) == ArgumentSignature::from(&signature)
});
if is_duplicate {
return Err(signature);
}
self.signatures = IntoIterator::into_iter(self.signatures)
.chain(Some(signature))
.collect();
Ok(self)
}
/// Returns the number of arguments the function expects.
///
/// For overloaded functions that can have a variable number of arguments,
/// this will return the minimum and maximum number of arguments.
///
/// Otherwise, the range will have the same start and end.
pub fn arg_count(&self) -> RangeInclusive<usize> {
self.signatures
.iter()
.map(SignatureInfo::arg_count)
.fold(RangeInclusive::new(usize::MAX, usize::MIN), |acc, count| {
RangeInclusive::new((*acc.start()).min(count), (*acc.end()).max(count))
})
}
/// The signatures of the function.
///
/// This is guaranteed to always contain at least one signature.
/// Overloaded functions will contain two or more.
pub fn signatures(&self) -> &[SignatureInfo] {
&self.signatures
}
/// Returns a wrapper around this info that implements [`Debug`] for pretty-printing the function.
///
/// This can be useful for more readable debugging and logging.
///
/// # Example
///
/// ```
/// # use bevy_reflect::func::{FunctionInfo, TypedFunction};
/// #
/// fn add(a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let info = add.get_function_info();
///
/// let pretty = info.pretty_printer();
/// assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32");
/// ```
pub fn pretty_printer(&self) -> PrettyPrintFunctionInfo {
PrettyPrintFunctionInfo::new(self)
}
/// Extend this [`FunctionInfo`] with another without checking for duplicates.
pub(super) fn extend_unchecked(&mut self, other: FunctionInfo) {
if self.name.is_none() {
self.name = other.name;
}
let signatures = core::mem::take(&mut self.signatures);
self.signatures = IntoIterator::into_iter(signatures)
.chain(IntoIterator::into_iter(other.signatures))
.collect();
}
}
impl From<SignatureInfo> for FunctionInfo {
fn from(info: SignatureInfo) -> Self {
Self::new(info)
}
}
impl TryFrom<Vec<SignatureInfo>> for FunctionInfo {
type Error = FunctionOverloadError;
fn try_from(signatures: Vec<SignatureInfo>) -> Result<Self, Self::Error> {
Self::try_from_iter(signatures)
}
}
impl<const N: usize> TryFrom<[SignatureInfo; N]> for FunctionInfo {
type Error = FunctionOverloadError;
fn try_from(signatures: [SignatureInfo; N]) -> Result<Self, Self::Error> {
Self::try_from_iter(signatures)
}
}
#[derive(Debug, Clone)]
pub struct SignatureInfo {
name: Option<Cow<'static, str>>,
args: Box<[ArgInfo]>,
return_info: ReturnInfo,
}
impl SignatureInfo {
/// Create a new [`SignatureInfo`] for a function with the given name.
pub fn named(name: impl Into<Cow<'static, str>>) -> Self { pub fn named(name: impl Into<Cow<'static, str>>) -> Self {
Self { Self {
name: Some(name.into()), name: Some(name.into()),
args: Vec::new(), args: Box::new([]),
return_info: ReturnInfo::new::<()>(), return_info: ReturnInfo::new::<()>(),
} }
} }
/// Create a new [`FunctionInfo`] with no name. /// Create a new [`SignatureInfo`] with no name.
/// ///
/// For the purposes of debugging and [registration], /// For the purposes of debugging and [registration],
/// it's recommended to use [`FunctionInfo::named`] instead. /// it's recommended to use [`Self::named`] instead.
/// ///
/// [registration]: crate::func::FunctionRegistry /// [registration]: crate::func::FunctionRegistry
pub fn anonymous() -> Self { pub fn anonymous() -> Self {
Self { Self {
name: None, name: None,
args: Vec::new(), args: Box::new([]),
return_info: ReturnInfo::new::<()>(), return_info: ReturnInfo::new::<()>(),
} }
} }
/// Create a new [`FunctionInfo`] from the given function.
pub fn from<F, Marker>(function: &F) -> Self
where
F: TypedFunction<Marker>,
{
function.get_function_info()
}
/// Set the name of the function. /// Set the name of the function.
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self { pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.name = Some(name.into()); self.name = Some(name.into());
@ -146,7 +244,9 @@ impl FunctionInfo {
name: impl Into<Cow<'static, str>>, name: impl Into<Cow<'static, str>>,
) -> Self { ) -> Self {
let index = self.args.len(); let index = self.args.len();
self.args.push(ArgInfo::new::<T>(index).with_name(name)); self.args = IntoIterator::into_iter(self.args)
.chain(Some(ArgInfo::new::<T>(index).with_name(name)))
.collect();
self self
} }
@ -157,7 +257,7 @@ impl FunctionInfo {
/// It's preferable to use [`Self::with_arg`] to add arguments to the function /// It's preferable to use [`Self::with_arg`] to add arguments to the function
/// as it will automatically set the index of the argument. /// as it will automatically set the index of the argument.
pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self { pub fn with_args(mut self, args: Vec<ArgInfo>) -> Self {
self.args = args; self.args = IntoIterator::into_iter(self.args).chain(args).collect();
self self
} }
@ -212,28 +312,6 @@ impl FunctionInfo {
pub fn return_info(&self) -> &ReturnInfo { pub fn return_info(&self) -> &ReturnInfo {
&self.return_info &self.return_info
} }
/// Returns a wrapper around this info that implements [`Debug`] for pretty-printing the function.
///
/// This can be useful for more readable debugging and logging.
///
/// # Example
///
/// ```
/// # use bevy_reflect::func::{FunctionInfo, TypedFunction};
/// #
/// fn add(a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let info = add.get_function_info();
///
/// let pretty = info.pretty_printer();
/// assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32");
/// ```
pub fn pretty_printer(&self) -> PrettyPrintFunctionInfo {
PrettyPrintFunctionInfo::new(self)
}
} }
/// Information about the return type of a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// Information about the return type of a [`DynamicFunction`] or [`DynamicFunctionMut`].
@ -309,6 +387,81 @@ impl<'a> PrettyPrintFunctionInfo<'a> {
} }
impl<'a> Debug for PrettyPrintFunctionInfo<'a> { impl<'a> Debug for PrettyPrintFunctionInfo<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
if self.include_fn_token {
write!(f, "fn")?;
if self.include_name {
write!(f, " ")?;
}
}
match (self.include_name, self.info.name()) {
(true, Some(name)) => write!(f, "{}", name)?,
(true, None) => write!(f, "_")?,
_ => {}
}
if self.info.is_overloaded() {
// `{(arg0: i32, arg1: i32) -> (), (arg0: f32, arg1: f32) -> ()}`
let mut set = f.debug_set();
for signature in self.info.signatures() {
set.entry(&PrettyPrintSignatureInfo::new(signature));
}
set.finish()
} else {
// `(arg0: i32, arg1: i32) -> ()`
PrettyPrintSignatureInfo::new(self.info.base()).fmt(f)
}
}
}
/// A wrapper around [`SignatureInfo`] that implements [`Debug`] for pretty-printing function signature information.
///
/// # Example
///
/// ```
/// # use bevy_reflect::func::{FunctionInfo, PrettyPrintSignatureInfo, TypedFunction};
/// #
/// fn add(a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let info = add.get_function_info();
///
/// let pretty = PrettyPrintSignatureInfo::new(info.base());
/// assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32");
/// ```
pub struct PrettyPrintSignatureInfo<'a> {
info: &'a SignatureInfo,
include_fn_token: bool,
include_name: bool,
}
impl<'a> PrettyPrintSignatureInfo<'a> {
/// Create a new pretty-printer for the given [`SignatureInfo`].
pub fn new(info: &'a SignatureInfo) -> Self {
Self {
info,
include_fn_token: false,
include_name: false,
}
}
/// Include the function name in the pretty-printed output.
pub fn include_name(mut self) -> Self {
self.include_name = true;
self
}
/// Include the `fn` token in the pretty-printed output.
pub fn include_fn_token(mut self) -> Self {
self.include_fn_token = true;
self
}
}
impl<'a> Debug for PrettyPrintSignatureInfo<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
if self.include_fn_token { if self.include_fn_token {
write!(f, "fn")?; write!(f, "fn")?;
@ -370,7 +523,7 @@ impl<'a> Debug for PrettyPrintFunctionInfo<'a> {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use bevy_reflect::func::{ArgList, FunctionInfo, ReflectFnMut, TypedFunction}; /// # use bevy_reflect::func::{ArgList, ReflectFnMut, TypedFunction};
/// # /// #
/// fn print(value: String) { /// fn print(value: String) {
/// println!("{}", value); /// println!("{}", value);
@ -378,9 +531,9 @@ impl<'a> Debug for PrettyPrintFunctionInfo<'a> {
/// ///
/// let info = print.get_function_info(); /// let info = print.get_function_info();
/// assert!(info.name().unwrap().ends_with("print")); /// assert!(info.name().unwrap().ends_with("print"));
/// assert_eq!(info.arg_count(), 1); /// assert!(info.arg_count().contains(&1));
/// assert_eq!(info.args()[0].type_path(), "alloc::string::String"); /// assert_eq!(info.base().args()[0].type_path(), "alloc::string::String");
/// assert_eq!(info.return_info().type_path(), "()"); /// assert_eq!(info.base().return_info().type_path(), "()");
/// ``` /// ```
/// ///
/// # Trait Parameters /// # Trait Parameters
@ -419,6 +572,7 @@ macro_rules! impl_typed_function {
Function: FnMut($($Arg),*) -> ReturnType, Function: FnMut($($Arg),*) -> ReturnType,
{ {
fn function_info() -> FunctionInfo { fn function_info() -> FunctionInfo {
FunctionInfo::new(
create_info::<Function>() create_info::<Function>()
.with_args({ .with_args({
#[allow(unused_mut)] #[allow(unused_mut)]
@ -431,6 +585,7 @@ macro_rules! impl_typed_function {
] ]
}) })
.with_return_info(ReturnInfo::new::<ReturnType>()) .with_return_info(ReturnInfo::new::<ReturnType>())
)
} }
} }
@ -442,7 +597,8 @@ macro_rules! impl_typed_function {
for<'a> &'a ReturnType: TypePath + GetOwnership, for<'a> &'a ReturnType: TypePath + GetOwnership,
Function: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a ReturnType, Function: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a ReturnType,
{ {
fn function_info() -> $crate::func::FunctionInfo { fn function_info() -> FunctionInfo {
FunctionInfo::new(
create_info::<Function>() create_info::<Function>()
.with_args({ .with_args({
#[allow(unused_mut)] #[allow(unused_mut)]
@ -456,6 +612,7 @@ macro_rules! impl_typed_function {
] ]
}) })
.with_return_info(ReturnInfo::new::<&ReturnType>()) .with_return_info(ReturnInfo::new::<&ReturnType>())
)
} }
} }
@ -468,6 +625,7 @@ macro_rules! impl_typed_function {
Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut ReturnType, Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut ReturnType,
{ {
fn function_info() -> FunctionInfo { fn function_info() -> FunctionInfo {
FunctionInfo::new(
create_info::<Function>() create_info::<Function>()
.with_args({ .with_args({
#[allow(unused_mut)] #[allow(unused_mut)]
@ -481,6 +639,7 @@ macro_rules! impl_typed_function {
] ]
}) })
.with_return_info(ReturnInfo::new::<&mut ReturnType>()) .with_return_info(ReturnInfo::new::<&mut ReturnType>())
)
} }
} }
@ -493,6 +652,7 @@ macro_rules! impl_typed_function {
Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a ReturnType, Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a ReturnType,
{ {
fn function_info() -> FunctionInfo { fn function_info() -> FunctionInfo {
FunctionInfo::new(
create_info::<Function>() create_info::<Function>()
.with_args({ .with_args({
#[allow(unused_mut)] #[allow(unused_mut)]
@ -506,6 +666,7 @@ macro_rules! impl_typed_function {
] ]
}) })
.with_return_info(ReturnInfo::new::<&ReturnType>()) .with_return_info(ReturnInfo::new::<&ReturnType>())
)
} }
} }
}; };
@ -531,13 +692,13 @@ all_tuples!(impl_typed_function, 0, 15, Arg, arg);
/// | Function pointer | `fn() -> String` | `None` | /// | Function pointer | `fn() -> String` | `None` |
/// ///
/// [`type_name`]: core::any::type_name /// [`type_name`]: core::any::type_name
fn create_info<F>() -> FunctionInfo { fn create_info<F>() -> SignatureInfo {
let name = core::any::type_name::<F>(); let name = core::any::type_name::<F>();
if name.ends_with("{{closure}}") || name.starts_with("fn(") { if name.ends_with("{{closure}}") || name.starts_with("fn(") {
FunctionInfo::anonymous() SignatureInfo::anonymous()
} else { } else {
FunctionInfo::named(name) SignatureInfo::named(name)
} }
} }
@ -562,10 +723,10 @@ mod tests {
info.name().unwrap(), info.name().unwrap(),
"bevy_reflect::func::info::tests::should_create_function_info::add" "bevy_reflect::func::info::tests::should_create_function_info::add"
); );
assert_eq!(info.arg_count(), 2); assert_eq!(info.base().arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32"); assert_eq!(info.base().args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32"); assert_eq!(info.base().args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "i32"); assert_eq!(info.base().return_info().type_path(), "i32");
} }
#[test] #[test]
@ -581,10 +742,10 @@ mod tests {
let info = add.get_function_info(); let info = add.get_function_info();
assert!(info.name().is_none()); assert!(info.name().is_none());
assert_eq!(info.arg_count(), 2); assert_eq!(info.base().arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32"); assert_eq!(info.base().args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32"); assert_eq!(info.base().args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "i32"); assert_eq!(info.base().return_info().type_path(), "i32");
} }
#[test] #[test]
@ -599,10 +760,10 @@ mod tests {
let info = add.get_function_info(); let info = add.get_function_info();
assert!(info.name().is_none()); assert!(info.name().is_none());
assert_eq!(info.arg_count(), 2); assert_eq!(info.base().arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32"); assert_eq!(info.base().args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32"); assert_eq!(info.base().args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "i32"); assert_eq!(info.base().return_info().type_path(), "i32");
} }
#[test] #[test]
@ -618,30 +779,30 @@ mod tests {
let info = add.get_function_info(); let info = add.get_function_info();
assert!(info.name().is_none()); assert!(info.name().is_none());
assert_eq!(info.arg_count(), 2); assert_eq!(info.base().arg_count(), 2);
assert_eq!(info.args()[0].type_path(), "i32"); assert_eq!(info.base().args()[0].type_path(), "i32");
assert_eq!(info.args()[1].type_path(), "i32"); assert_eq!(info.base().args()[1].type_path(), "i32");
assert_eq!(info.return_info().type_path(), "()"); assert_eq!(info.base().return_info().type_path(), "()");
} }
#[test] #[test]
fn should_pretty_print_info() { fn should_pretty_print_info() {
fn add(a: i32, b: i32) -> i32 { // fn add(a: i32, b: i32) -> i32 {
a + b // a + b
} // }
//
let info = add.get_function_info().with_name("add"); // let info = add.get_function_info().with_name("add");
//
let pretty = info.pretty_printer(); // let pretty = info.pretty_printer();
assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32"); // assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32");
//
let pretty = info.pretty_printer().include_fn_token(); // let pretty = info.pretty_printer().include_fn_token();
assert_eq!(format!("{:?}", pretty), "fn(_: i32, _: i32) -> i32"); // assert_eq!(format!("{:?}", pretty), "fn(_: i32, _: i32) -> i32");
//
let pretty = info.pretty_printer().include_name(); // let pretty = info.pretty_printer().include_name();
assert_eq!(format!("{:?}", pretty), "add(_: i32, _: i32) -> i32"); // assert_eq!(format!("{:?}", pretty), "add(_: i32, _: i32) -> i32");
//
let pretty = info.pretty_printer().include_fn_token().include_name(); // let pretty = info.pretty_printer().include_fn_token().include_name();
assert_eq!(format!("{:?}", pretty), "fn add(_: i32, _: i32) -> i32"); // assert_eq!(format!("{:?}", pretty), "fn add(_: i32, _: i32) -> i32");
} }
} }

View file

@ -1,16 +1,18 @@
//! Function signature types. //! Function signature types.
//! //!
//! Function signatures differ from [`FunctionInfo`] in that they are only concerned //! Function signatures differ from [`FunctionInfo`] and [`SignatureInfo`] in that they
//! about the types and order of the arguments and return type of a function. //! are only concerned about the types and order of the arguments and return type of a function.
//! //!
//! The names of arguments do not matter, //! The names of arguments do not matter,
//! nor does any other information about the function such as its name or other attributes. //! nor does any other information about the function such as its name or other attributes.
//! //!
//! This makes signatures useful for comparing or hashing functions strictly based on their //! This makes signatures useful for comparing or hashing functions strictly based on their
//! arguments and return type. //! arguments and return type.
//!
//! [`FunctionInfo`]: crate::func::info::FunctionInfo
use crate::func::args::ArgInfo; use crate::func::args::ArgInfo;
use crate::func::{ArgList, FunctionInfo}; use crate::func::{ArgList, SignatureInfo};
use crate::Type; use crate::Type;
use core::borrow::Borrow; use core::borrow::Borrow;
use core::fmt::{Debug, Formatter}; use core::fmt::{Debug, Formatter};
@ -48,7 +50,7 @@ impl Debug for Signature {
} }
} }
impl<T: Borrow<FunctionInfo>> From<T> for Signature { impl<T: Borrow<SignatureInfo>> From<T> for Signature {
fn from(info: T) -> Self { fn from(info: T) -> Self {
let info = info.borrow(); let info = info.borrow();
Self::new(ArgumentSignature::from(info), *info.return_info().ty()) Self::new(ArgumentSignature::from(info), *info.return_info().ty())
@ -94,7 +96,7 @@ impl FromIterator<Type> for ArgumentSignature {
} }
} }
impl<T: Borrow<FunctionInfo>> From<T> for ArgumentSignature { impl<T: Borrow<SignatureInfo>> From<T> for ArgumentSignature {
fn from(info: T) -> Self { fn from(info: T) -> Self {
Self( Self(
info.borrow() info.borrow()
@ -137,7 +139,7 @@ mod tests {
} }
let info = add.get_function_info(); let info = add.get_function_info();
let signature = Signature::from(&info); let signature = Signature::from(info.base());
assert_eq!(signature.args().0.len(), 2); assert_eq!(signature.args().0.len(), 2);
assert_eq!(signature.args().0[0], Type::of::<i32>()); assert_eq!(signature.args().0[0], Type::of::<i32>());

View file

@ -8,8 +8,8 @@
use bevy::reflect::{ use bevy::reflect::{
func::{ func::{
ArgList, DynamicFunction, DynamicFunctionMut, FunctionInfo, FunctionResult, IntoFunction, ArgList, DynamicFunction, DynamicFunctionMut, FunctionResult, IntoFunction,
IntoFunctionMut, Return, IntoFunctionMut, Return, SignatureInfo,
}, },
PartialReflect, Reflect, PartialReflect, Reflect,
}; };
@ -193,7 +193,7 @@ fn main() {
// This makes it easier to debug and is also required for function registration. // This makes it easier to debug and is also required for function registration.
// We can either give it a custom name or use the function's type name as // We can either give it a custom name or use the function's type name as
// derived from `std::any::type_name_of_val`. // derived from `std::any::type_name_of_val`.
FunctionInfo::named(std::any::type_name_of_val(&get_or_insert)) SignatureInfo::named(std::any::type_name_of_val(&get_or_insert))
// We can always change the name if needed. // We can always change the name if needed.
// It's a good idea to also ensure that the name is unique, // It's a good idea to also ensure that the name is unique,
// such as by using its type name or by prefixing it with your crate name. // such as by using its type name or by prefixing it with your crate name.