Simplified FunctionMap

This commit is contained in:
Gino Valente 2024-09-03 18:37:45 -07:00
parent 8ce37d975a
commit 38b3cbf5c5
5 changed files with 327 additions and 200 deletions

View file

@ -2,11 +2,9 @@ use crate::{
self as bevy_reflect, self as bevy_reflect,
__macro_exports::RegisterForReflection, __macro_exports::RegisterForReflection,
func::{ func::{
args::ArgList, args::ArgList, function_map::FunctionMap, info::FunctionInfoType,
function_map::{merge_function_map, FunctionMap}, signature::ArgumentSignature, DynamicFunctionMut, Function, FunctionError, FunctionResult,
info::FunctionInfoType, IntoFunction, IntoFunctionMut,
signature::ArgumentSignature,
DynamicFunctionMut, Function, FunctionError, FunctionResult, IntoFunction, IntoFunctionMut,
}, },
serde::Serializable, serde::Serializable,
ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
@ -49,7 +47,7 @@ type ArcFn<'env> = Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send
/// Most of the time, a [`DynamicFunction`] can be created using the [`IntoFunction`] trait: /// Most of the time, a [`DynamicFunction`] can be created using the [`IntoFunction`] trait:
/// ///
/// ``` /// ```
/// # use bevy_reflect::func::{ArgList, DynamicFunction, FunctionInfo, IntoFunction}; /// # use bevy_reflect::func::{ArgList, DynamicFunction, IntoFunction};
/// # /// #
/// fn add(a: i32, b: i32) -> i32 { /// fn add(a: i32, b: i32) -> i32 {
/// a + b /// a + b
@ -70,7 +68,6 @@ type ArcFn<'env> = Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send
/// [module-level documentation]: crate::func /// [module-level documentation]: crate::func
pub struct DynamicFunction<'env> { pub struct DynamicFunction<'env> {
pub(super) name: Option<Cow<'static, str>>, pub(super) name: Option<Cow<'static, str>>,
pub(super) info: FunctionInfoType,
pub(super) function_map: FunctionMap<ArcFn<'env>>, pub(super) function_map: FunctionMap<ArcFn<'env>>,
} }
@ -104,16 +101,21 @@ impl<'env> DynamicFunction<'env> {
FunctionInfoType::Standard(info) => info.name().cloned(), FunctionInfoType::Standard(info) => info.name().cloned(),
FunctionInfoType::Overloaded(_) => None, FunctionInfoType::Overloaded(_) => None,
}, },
function_map: match &info { function_map: match info {
FunctionInfoType::Standard(_) => FunctionMap::Standard(func), FunctionInfoType::Standard(info) => FunctionMap {
FunctionInfoType::Overloaded(infos) => { functions: vec![func],
FunctionMap::Overloaded(HashMap::from_iter(infos.iter().map(|info| { indices: HashMap::from([(ArgumentSignature::from(&info), 0)]),
let sig = ArgumentSignature::from(info); info: FunctionInfoType::Standard(info),
(sig, func.clone()) },
}))) FunctionInfoType::Overloaded(infos) => FunctionMap {
} functions: vec![func],
indices: infos
.iter()
.map(|info| (ArgumentSignature::from(info), 0))
.collect(),
info: FunctionInfoType::Overloaded(infos),
},
}, },
info,
} }
} }
@ -144,6 +146,10 @@ impl<'env> DynamicFunction<'env> {
/// ///
/// Overloaded functions retain the [name] of the original function. /// Overloaded functions retain the [name] of the original function.
/// ///
/// # Panics
///
/// Panics if the function, `F`, contains a signature already found in this function.
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@ -202,6 +208,23 @@ impl<'env> DynamicFunction<'env> {
/// assert_eq!(result.try_take::<i32>().unwrap(), 200); /// assert_eq!(result.try_take::<i32>().unwrap(), 200);
/// ``` /// ```
/// ///
///```should_panic
/// # use bevy_reflect::func::IntoFunction;
///
/// fn add(a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// fn sub(a: i32, b: i32) -> i32 {
/// a - b
/// }
///
/// let mut func = add.into_function();
///
/// // This will panic because the function already has an argument signature for `(i32, i32)`:
/// func = func.with_overload(sub);
/// ```
///
/// [argument signature]: ArgumentSignature /// [argument signature]: ArgumentSignature
/// [name]: Self::name /// [name]: Self::name
pub fn with_overload<'a, F: IntoFunction<'a, Marker>, Marker>( pub fn with_overload<'a, F: IntoFunction<'a, Marker>, Marker>(
@ -213,19 +236,15 @@ impl<'env> DynamicFunction<'env> {
{ {
let function = function.into_function(); let function = function.into_function();
let name = self.name; let name = self.name.clone();
let (function_map, info) = merge_function_map( let mut function_map = self.function_map;
self.function_map, function_map
self.info, .merge(function.function_map)
function.function_map, .unwrap_or_else(|err| {
function.info, panic!("{}", err);
); });
DynamicFunction { DynamicFunction { name, function_map }
name,
info,
function_map,
}
} }
/// Call the function with the given arguments. /// Call the function with the given arguments.
@ -252,35 +271,25 @@ impl<'env> DynamicFunction<'env> {
/// ///
/// The function itself may also return any errors it needs to. /// The function itself may also return any errors it needs to.
pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> { pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {
let expected_arg_count = self.info.arg_count(); let expected_arg_count = self.function_map.info.arg_count();
let received_arg_count = args.len(); let received_arg_count = args.len();
match self.function_map { if matches!(self.function_map.info, FunctionInfoType::Standard(_))
FunctionMap::Standard(ref func) => { && expected_arg_count != received_arg_count
if expected_arg_count != received_arg_count { {
Err(FunctionError::ArgCountMismatch { Err(FunctionError::ArgCountMismatch {
expected: expected_arg_count, expected: expected_arg_count,
received: received_arg_count, received: received_arg_count,
}) })
} else { } else {
func(args) let func = self.function_map.get(&args)?;
} func(args)
}
FunctionMap::Overloaded(ref map) => {
let sig = ArgumentSignature::from(&args);
let func = map.get(&sig).ok_or_else(|| FunctionError::NoOverload {
expected: map.keys().cloned().collect(),
received: sig,
})?;
func(args)
}
} }
} }
/// Returns the function info. /// Returns the function info.
pub fn info(&self) -> &FunctionInfoType { pub fn info(&self) -> &FunctionInfoType {
&self.info &self.function_map.info
} }
/// The name of the function. /// The name of the function.
@ -308,7 +317,7 @@ impl Function for DynamicFunction<'static> {
} }
fn info(&self) -> &FunctionInfoType { fn info(&self) -> &FunctionInfoType {
&self.info &self.function_map.info
} }
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> { fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {
@ -444,7 +453,6 @@ impl<'env> Clone for DynamicFunction<'env> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
name: self.name.clone(), name: self.name.clone(),
info: self.info.clone(),
function_map: self.function_map.clone(), function_map: self.function_map.clone(),
} }
} }
@ -467,7 +475,7 @@ impl<'env> IntoFunctionMut<'env, ()> for DynamicFunction<'env> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::func::{FunctionInfo, IntoReturn}; use crate::func::{FunctionError, FunctionInfo, IntoReturn};
use crate::Type; use crate::Type;
use bevy_utils::HashSet; use bevy_utils::HashSet;
use std::ops::Add; use std::ops::Add;

View file

@ -5,11 +5,10 @@ use core::fmt::{Debug, Formatter};
use alloc::{boxed::Box, format, vec}; use alloc::{boxed::Box, format, vec};
use crate::func::{ use crate::func::{
args::ArgList, args::ArgList, function_map::FunctionMap, signature::ArgumentSignature, DynamicFunction,
function_map::{merge_function_map, FunctionMap}, FunctionError, FunctionInfoType, FunctionResult, IntoFunctionMut,
signature::ArgumentSignature,
DynamicFunction, FunctionError, FunctionInfoType, FunctionResult, IntoFunctionMut,
}; };
use bevy_utils::HashMap;
/// A [`Box`] containing a callback to a reflected function. /// A [`Box`] containing a callback to a reflected function.
type BoxFnMut<'env> = Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>; type BoxFnMut<'env> = Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>;
@ -72,7 +71,6 @@ type BoxFnMut<'env> = Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> +
/// [module-level documentation]: crate::func /// [module-level documentation]: crate::func
pub struct DynamicFunctionMut<'env> { pub struct DynamicFunctionMut<'env> {
name: Option<Cow<'static, str>>, name: Option<Cow<'static, str>>,
info: FunctionInfoType,
function_map: FunctionMap<BoxFnMut<'env>>, function_map: FunctionMap<BoxFnMut<'env>>,
} }
@ -99,13 +97,28 @@ impl<'env> DynamicFunctionMut<'env> {
) -> Self { ) -> Self {
let info = info.try_into().unwrap(); let info = info.try_into().unwrap();
let func: BoxFnMut = Box::new(func);
Self { Self {
name: match &info { name: match &info {
FunctionInfoType::Standard(info) => info.name().cloned(), FunctionInfoType::Standard(info) => info.name().cloned(),
FunctionInfoType::Overloaded(_) => None, FunctionInfoType::Overloaded(_) => None,
}, },
info, function_map: match info {
function_map: FunctionMap::Standard(Box::new(func)), FunctionInfoType::Standard(info) => FunctionMap {
functions: vec![func],
indices: HashMap::from([(ArgumentSignature::from(&info), 0)]),
info: FunctionInfoType::Standard(info),
},
FunctionInfoType::Overloaded(infos) => FunctionMap {
functions: vec![func],
indices: infos
.iter()
.map(|info| (ArgumentSignature::from(info), 0))
.collect(),
info: FunctionInfoType::Overloaded(infos),
},
},
} }
} }
@ -141,6 +154,10 @@ impl<'env> DynamicFunctionMut<'env> {
/// However, it's still possible to overload functions that do not capture their environment mutably, /// However, it's still possible to overload functions that do not capture their environment mutably,
/// or those that maintain mutually exclusive mutable references to their environment. /// or those that maintain mutually exclusive mutable references to their environment.
/// ///
/// # Panics
///
/// Panics if the function, `F`, contains a signature already found in this function.
///
/// # Example /// # Example
/// ///
/// ``` /// ```
@ -181,19 +198,13 @@ impl<'env> DynamicFunctionMut<'env> {
{ {
let function = function.into_function_mut(); let function = function.into_function_mut();
let name = self.name; let name = self.name.clone();
let (function_map, info) = merge_function_map( let mut function_map = self.function_map;
self.function_map, function_map
self.info, .merge(function.function_map)
function.function_map, .unwrap_or_else(|_| todo!());
function.info,
);
DynamicFunctionMut { DynamicFunctionMut { name, function_map }
name,
info,
function_map,
}
} }
/// Call the function with the given arguments. /// Call the function with the given arguments.
@ -228,31 +239,19 @@ impl<'env> DynamicFunctionMut<'env> {
/// ///
/// [`call_once`]: DynamicFunctionMut::call_once /// [`call_once`]: DynamicFunctionMut::call_once
pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> { pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> {
let expected_arg_count = self.info.arg_count(); let expected_arg_count = self.function_map.info.arg_count();
let received_arg_count = args.len(); let received_arg_count = args.len();
match self.function_map { if matches!(self.function_map.info, FunctionInfoType::Standard(_))
FunctionMap::Standard(ref mut func) => { && expected_arg_count != received_arg_count
if expected_arg_count != received_arg_count { {
Err(FunctionError::ArgCountMismatch { Err(FunctionError::ArgCountMismatch {
expected: expected_arg_count, expected: expected_arg_count,
received: received_arg_count, received: received_arg_count,
}) })
} else { } else {
func(args) let func = self.function_map.get_mut(&args)?;
} func(args)
}
FunctionMap::Overloaded(ref mut map) => {
let sig = ArgumentSignature::from(&args);
if let Some(func) = map.get_mut(&sig) {
func(args)
} else {
Err(FunctionError::NoOverload {
expected: map.keys().cloned().collect(),
received: sig,
})
}
}
} }
} }
@ -290,7 +289,7 @@ impl<'env> DynamicFunctionMut<'env> {
/// Returns the function info. /// Returns the function info.
pub fn info(&self) -> &FunctionInfoType { pub fn info(&self) -> &FunctionInfoType {
&self.info &self.function_map.info
} }
/// The name of the function. /// The name of the function.
@ -346,15 +345,15 @@ impl<'env> From<DynamicFunction<'env>> for DynamicFunctionMut<'env> {
fn from(function: DynamicFunction<'env>) -> Self { fn from(function: DynamicFunction<'env>) -> Self {
Self { Self {
name: function.name, name: function.name,
info: function.info, function_map: FunctionMap {
function_map: match function.function_map { info: function.function_map.info,
FunctionMap::Standard(func) => FunctionMap::Standard(arc_to_box(func)), indices: function.function_map.indices,
FunctionMap::Overloaded(functions) => FunctionMap::Overloaded( functions: function
functions .function_map
.into_iter() .functions
.map(|(name, func)| (name, arc_to_box(func))) .into_iter()
.collect(), .map(arc_to_box)
), .collect(),
}, },
} }
} }
@ -364,7 +363,7 @@ impl<'env> From<DynamicFunction<'env>> for DynamicFunctionMut<'env> {
/// ///
/// This is needed to help the compiler infer the correct types. /// This is needed to help the compiler infer the correct types.
fn arc_to_box<'env>( fn arc_to_box<'env>(
f: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + 'env>, f: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>,
) -> BoxFnMut<'env> { ) -> BoxFnMut<'env> {
Box::new(move |args| f(args)) Box::new(move |args| f(args))
} }

View file

@ -43,6 +43,13 @@ pub type FunctionResult<'a> = Result<Return<'a>, FunctionError>;
#[error("expected a `FunctionInfo` but found none")] #[error("expected a `FunctionInfo` but found none")]
pub struct MissingFunctionInfoError; pub struct MissingFunctionInfoError;
/// An error that occurs when attempting to add a function overload with a duplicate signature.
#[derive(Debug, Error, PartialEq)]
#[error("could not add function overload: duplicate found for signature `{signature:?}`")]
pub struct FunctionOverloadError {
pub signature: ArgumentSignature,
}
/// An error that occurs when registering a function into a [`FunctionRegistry`]. /// An error that occurs when registering a function into a [`FunctionRegistry`].
/// ///
/// [`FunctionRegistry`]: crate::func::FunctionRegistry /// [`FunctionRegistry`]: crate::func::FunctionRegistry

View file

@ -1,102 +1,203 @@
use crate::func::signature::ArgumentSignature; use crate::func::signature::ArgumentSignature;
use crate::func::FunctionInfoType; use crate::func::{ArgList, FunctionError, FunctionInfoType, FunctionOverloadError};
use bevy_utils::HashMap; use bevy_utils::HashMap;
/// A helper type for storing either a single function or a mapping of overloaded functions. /// A helper type for storing a mapping of overloaded functions
#[derive(Clone)] /// along with the corresponding [function information].
pub(super) enum FunctionMap<F> { ///
Standard(F), /// [function information]: FunctionInfoType
Overloaded(HashMap<ArgumentSignature, F>), #[derive(Clone, Debug)]
pub(super) struct FunctionMap<F> {
pub info: FunctionInfoType,
pub functions: Vec<F>,
pub indices: HashMap<ArgumentSignature, usize>,
} }
/// Merges the given [`FunctionMap`]s and [`FunctionInfoType`]s into a new [`FunctionMap`] and [`FunctionInfoType`]. impl<F> FunctionMap<F> {
/// /// Get a reference to a function in the map.
/// # Panics ///
/// /// If there is only one function in the map, it will be returned.
/// Panics if a [`FunctionMap`]'s corresponding [`FunctionInfoType`] does not match its overload status. /// Otherwise, the function will be selected based on the arguments provided.
pub(super) fn merge_function_map<F>( ///
map_a: FunctionMap<F>, /// If no overload matches the provided arguments, an error will be returned.
info_a: FunctionInfoType, pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> {
map_b: FunctionMap<F>, if self.functions.len() == 1 {
info_b: FunctionInfoType, Ok(&self.functions[0])
) -> (FunctionMap<F>, FunctionInfoType) { } else {
match (map_a, info_a, map_b, info_b) { let signature = ArgumentSignature::from(args);
( self.indices
FunctionMap::Standard(old), .get(&signature)
FunctionInfoType::Standard(info_a), .map(|index| &self.functions[*index])
FunctionMap::Standard(new), .ok_or_else(|| FunctionError::NoOverload {
FunctionInfoType::Standard(info_b), expected: self.indices.keys().cloned().collect(),
) => { received: signature,
let sig_a = ArgumentSignature::from(&info_a); })
let sig_b = ArgumentSignature::from(&info_b);
if sig_a == sig_b {
(
FunctionMap::Standard(new),
FunctionInfoType::Standard(info_b),
)
} else {
(
FunctionMap::Overloaded(HashMap::from([(sig_a, old), (sig_b, new)])),
FunctionInfoType::Overloaded(Box::new([info_a, info_b])),
)
}
}
(
FunctionMap::Overloaded(old),
FunctionInfoType::Overloaded(info_a),
FunctionMap::Standard(new),
FunctionInfoType::Standard(info_b),
) => {
let sig_b = ArgumentSignature::from(&info_b);
let mut map = old;
map.insert(sig_b, new);
let mut info = Vec::from_iter(info_a);
info.push(info_b);
(
FunctionMap::Overloaded(map),
FunctionInfoType::Overloaded(info.into_boxed_slice()),
)
}
(
FunctionMap::Standard(old),
FunctionInfoType::Standard(info_a),
FunctionMap::Overloaded(new),
FunctionInfoType::Overloaded(info_b),
) => {
let sig_a = ArgumentSignature::from(&info_a);
let mut map = new;
map.insert(sig_a, old);
let mut info = vec![info_a];
info.extend(info_b);
(
FunctionMap::Overloaded(map),
FunctionInfoType::Overloaded(info.into_boxed_slice()),
)
}
(
FunctionMap::Overloaded(map1),
FunctionInfoType::Overloaded(info_a),
FunctionMap::Overloaded(map2),
FunctionInfoType::Overloaded(info_b),
) => {
let mut map = map1;
map.extend(map2);
let mut info = Vec::from_iter(info_a);
info.extend(info_b);
(
FunctionMap::Overloaded(map),
FunctionInfoType::Overloaded(info.into_boxed_slice()),
)
}
_ => {
panic!("`FunctionMap` and `FunctionInfoType` mismatch");
} }
} }
/// Get a mutable reference to a function in the map.
///
/// If there is only one function in the map, it will be returned.
/// Otherwise, the function will be selected based on the arguments provided.
///
/// If no overload matches the provided arguments, an error will be returned.
pub fn get_mut(&mut self, args: &ArgList) -> Result<&mut F, FunctionError> {
if self.functions.len() == 1 {
Ok(&mut self.functions[0])
} else {
let signature = ArgumentSignature::from(args);
self.indices
.get(&signature)
.map(|index| &mut self.functions[*index])
.ok_or_else(|| FunctionError::NoOverload {
expected: self.indices.keys().cloned().collect(),
received: signature,
})
}
}
/// 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 and the original map will remain unchanged.
pub fn merge(&mut self, other: Self) -> Result<(), FunctionOverloadError> {
// === Function Map === //
let mut other_indices = HashMap::new();
for (sig, index) in other.indices {
if self.indices.contains_key(&sig) {
return Err(FunctionOverloadError { signature: sig });
}
other_indices.insert(sig, self.functions.len() + index);
}
// === Function Info === //
let mut other_infos = Vec::new();
for info in other.info.into_iter() {
let sig = ArgumentSignature::from(&info);
if self.indices.contains_key(&sig) {
return Err(FunctionOverloadError { signature: sig });
}
other_infos.push(info);
}
// === Update === //
self.indices.extend(other_indices);
self.functions.extend(other.functions);
self.info = match &self.info {
FunctionInfoType::Standard(info) => FunctionInfoType::Overloaded(
std::iter::once(info.clone()).chain(other_infos).collect(),
),
FunctionInfoType::Overloaded(infos) => {
FunctionInfoType::Overloaded(infos.iter().cloned().chain(other_infos).collect())
}
};
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::func::FunctionInfo;
use crate::Type;
#[test]
fn should_merge_function_maps() {
let mut map_a = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
FunctionInfo::anonymous().with_arg::<i32>("arg0"),
])),
functions: vec!['a', 'b', 'c'],
indices: HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
]),
};
let map_b = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<u16>("arg0"),
FunctionInfo::anonymous().with_arg::<u32>("arg0"),
])),
functions: vec!['d', 'e', 'f'],
indices: HashMap::from([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 2),
]),
};
map_a.merge(map_b).unwrap();
assert_eq!(map_a.functions, vec!['a', 'b', 'c', 'd', 'e', 'f']);
assert_eq!(
map_a.indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 3),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 4),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 5),
])
);
}
#[test]
fn should_return_error_on_duplicate_signature() {
let mut map_a = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
FunctionInfo::anonymous().with_arg::<i32>("arg0"),
])),
functions: vec!['a', 'b', 'c'],
indices: HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
]),
};
let map_b = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
FunctionInfo::anonymous().with_arg::<u32>("arg0"),
])),
functions: vec!['d', 'e', 'f'],
indices: HashMap::from([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 2),
]),
};
let result = map_a.merge(map_b);
assert_eq!(
result.unwrap_err(),
FunctionOverloadError {
signature: ArgumentSignature::from_iter([Type::of::<i16>()])
}
);
// Assert that the original map remains unchanged:
assert_eq!(map_a.functions, vec!['a', 'b', 'c']);
assert_eq!(
map_a.indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
])
);
}
} }

View file

@ -47,6 +47,18 @@ impl TryFrom<Vec<FunctionInfo>> for FunctionInfoType {
} }
} }
impl IntoIterator for FunctionInfoType {
type Item = FunctionInfo;
type IntoIter = vec::IntoIter<FunctionInfo>;
fn into_iter(self) -> Self::IntoIter {
match self {
FunctionInfoType::Standard(info) => vec![info].into_iter(),
FunctionInfoType::Overloaded(infos) => infos.into_vec().into_iter(),
}
}
}
impl FunctionInfoType { impl FunctionInfoType {
pub fn arg_count(&self) -> usize { pub fn arg_count(&self) -> usize {
match self { match self {