Optimized FunctionMap

This commit is contained in:
Gino Valente 2024-09-04 16:21:13 -07:00
parent b3b6671d9c
commit 5923cfa0ec
5 changed files with 309 additions and 190 deletions

View file

@ -12,7 +12,6 @@ use crate::{
}; };
use alloc::{borrow::Cow, boxed::Box, sync::Arc}; use alloc::{borrow::Cow, boxed::Box, sync::Arc};
use bevy_reflect_derive::impl_type_path; use bevy_reflect_derive::impl_type_path;
use bevy_utils::HashMap;
use core::fmt::{Debug, Formatter}; use core::fmt::{Debug, Formatter};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
@ -90,7 +89,7 @@ impl<'env> DynamicFunction<'env> {
/// [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, Error: Debug>, info: impl TryInto<FunctionInfoType<'static>, Error: Debug>,
) -> Self { ) -> Self {
let info = info.try_into().unwrap(); let info = info.try_into().unwrap();
@ -102,19 +101,14 @@ impl<'env> DynamicFunction<'env> {
FunctionInfoType::Overloaded(_) => None, FunctionInfoType::Overloaded(_) => None,
}, },
function_map: match info { function_map: match info {
FunctionInfoType::Standard(info) => FunctionMap { FunctionInfoType::Standard(info) => FunctionMap::Single(func, info.into_owned()),
functions: vec![func], FunctionInfoType::Overloaded(infos) => {
indices: HashMap::from([(ArgumentSignature::from(&info), 0)]), let indices = infos
info: FunctionInfoType::Standard(info),
},
FunctionInfoType::Overloaded(infos) => FunctionMap {
functions: vec![func],
indices: infos
.iter() .iter()
.map(|info| (ArgumentSignature::from(info), 0)) .map(|info| (ArgumentSignature::from(info), 0))
.collect(), .collect();
info: FunctionInfoType::Overloaded(infos), FunctionMap::Overloaded(vec![func], infos.into_owned(), indices)
}, }
}, },
} }
} }
@ -302,10 +296,10 @@ 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.function_map.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();
if matches!(self.function_map.info, FunctionInfoType::Standard(_)) if matches!(self.function_map.info(), FunctionInfoType::Standard(_))
&& expected_arg_count != received_arg_count && expected_arg_count != received_arg_count
{ {
Err(FunctionError::ArgCountMismatch { Err(FunctionError::ArgCountMismatch {
@ -319,8 +313,8 @@ impl<'env> DynamicFunction<'env> {
} }
/// Returns the function info. /// Returns the function info.
pub fn info(&self) -> &FunctionInfoType { pub fn info(&self) -> FunctionInfoType {
&self.function_map.info self.function_map.info()
} }
/// The name of the function. /// The name of the function.
@ -347,8 +341,8 @@ impl Function for DynamicFunction<'static> {
self.name.as_ref() self.name.as_ref()
} }
fn info(&self) -> &FunctionInfoType { fn info(&self) -> FunctionInfoType {
&self.function_map.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> {

View file

@ -8,7 +8,6 @@ use crate::func::{
args::ArgList, function_map::FunctionMap, signature::ArgumentSignature, DynamicFunction, args::ArgList, function_map::FunctionMap, signature::ArgumentSignature, DynamicFunction,
FunctionError, FunctionInfoType, FunctionOverloadError, FunctionResult, IntoFunctionMut, FunctionError, FunctionInfoType, FunctionOverloadError, 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>;
@ -93,7 +92,7 @@ impl<'env> DynamicFunctionMut<'env> {
/// [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, Error: Debug>, info: impl TryInto<FunctionInfoType<'static>, Error: Debug>,
) -> Self { ) -> Self {
let info = info.try_into().unwrap(); let info = info.try_into().unwrap();
@ -105,19 +104,14 @@ impl<'env> DynamicFunctionMut<'env> {
FunctionInfoType::Overloaded(_) => None, FunctionInfoType::Overloaded(_) => None,
}, },
function_map: match info { function_map: match info {
FunctionInfoType::Standard(info) => FunctionMap { FunctionInfoType::Standard(info) => FunctionMap::Single(func, info.into_owned()),
functions: vec![func], FunctionInfoType::Overloaded(infos) => {
indices: HashMap::from([(ArgumentSignature::from(&info), 0)]), let indices = infos
info: FunctionInfoType::Standard(info),
},
FunctionInfoType::Overloaded(infos) => FunctionMap {
functions: vec![func],
indices: infos
.iter() .iter()
.map(|info| (ArgumentSignature::from(info), 0)) .map(|info| (ArgumentSignature::from(info), 0))
.collect(), .collect();
info: FunctionInfoType::Overloaded(infos), FunctionMap::Overloaded(vec![func], infos.into_owned(), indices)
}, }
}, },
} }
} }
@ -272,10 +266,10 @@ 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.function_map.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();
if matches!(self.function_map.info, FunctionInfoType::Standard(_)) if matches!(self.function_map.info(), FunctionInfoType::Standard(_))
&& expected_arg_count != received_arg_count && expected_arg_count != received_arg_count
{ {
Err(FunctionError::ArgCountMismatch { Err(FunctionError::ArgCountMismatch {
@ -321,8 +315,8 @@ impl<'env> DynamicFunctionMut<'env> {
} }
/// Returns the function info. /// Returns the function info.
pub fn info(&self) -> &FunctionInfoType { pub fn info(&self) -> FunctionInfoType {
&self.function_map.info self.function_map.info()
} }
/// The name of the function. /// The name of the function.
@ -378,15 +372,13 @@ 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,
function_map: FunctionMap { function_map: match function.function_map {
info: function.function_map.info, FunctionMap::Single(func, info) => FunctionMap::Single(arc_to_box(func), info),
indices: function.function_map.indices, FunctionMap::Overloaded(funcs, infos, indices) => FunctionMap::Overloaded(
functions: function funcs.into_iter().map(arc_to_box).collect(),
.function_map infos,
.functions indices,
.into_iter() ),
.map(arc_to_box)
.collect(),
}, },
} }
} }

View file

@ -53,7 +53,7 @@ pub trait Function: PartialReflect + Debug {
} }
/// The [`FunctionInfoType`] for this function. /// The [`FunctionInfoType`] for this function.
fn info(&self) -> &FunctionInfoType; fn info(&self) -> FunctionInfoType;
/// 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

@ -1,16 +1,16 @@
use crate::func::signature::ArgumentSignature; use crate::func::signature::ArgumentSignature;
use crate::func::{ArgList, FunctionError, FunctionInfoType, FunctionOverloadError}; use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionInfoType, FunctionOverloadError};
use alloc::borrow::Cow;
use bevy_utils::HashMap; use bevy_utils::HashMap;
/// A helper type for storing a mapping of overloaded functions /// A helper type for storing a mapping of overloaded functions
/// along with the corresponding [function information]. /// along with the corresponding [function information].
/// ///
/// [function information]: FunctionInfoType /// [function information]: FunctionInfo
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(super) struct FunctionMap<F> { pub(super) enum FunctionMap<F> {
pub info: FunctionInfoType, Single(F, FunctionInfo),
pub functions: Vec<F>, Overloaded(Vec<F>, Vec<FunctionInfo>, HashMap<ArgumentSignature, usize>),
pub indices: HashMap<ArgumentSignature, usize>,
} }
impl<F> FunctionMap<F> { impl<F> FunctionMap<F> {
@ -21,17 +21,18 @@ impl<F> FunctionMap<F> {
/// ///
/// If no overload matches the provided arguments, an error will be returned. /// If no overload matches the provided arguments, an error will be returned.
pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> { pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> {
if self.functions.len() == 1 { match self {
Ok(&self.functions[0]) Self::Single(function, _) => Ok(function),
} else { Self::Overloaded(functions, _, indices) => {
let signature = ArgumentSignature::from(args); let signature = ArgumentSignature::from(args);
self.indices indices
.get(&signature) .get(&signature)
.map(|index| &self.functions[*index]) .map(|index| &functions[*index])
.ok_or_else(|| FunctionError::NoOverload { .ok_or_else(|| FunctionError::NoOverload {
expected: self.indices.keys().cloned().collect(), expected: indices.keys().cloned().collect(),
received: signature, received: signature,
}) })
}
} }
} }
@ -42,17 +43,26 @@ impl<F> FunctionMap<F> {
/// ///
/// If no overload matches the provided arguments, an error will be returned. /// If no overload matches the provided arguments, an error will be returned.
pub fn get_mut(&mut self, args: &ArgList) -> Result<&mut F, FunctionError> { pub fn get_mut(&mut self, args: &ArgList) -> Result<&mut F, FunctionError> {
if self.functions.len() == 1 { match self {
Ok(&mut self.functions[0]) Self::Single(function, _) => Ok(function),
} else { Self::Overloaded(functions, _, indices) => {
let signature = ArgumentSignature::from(args); let signature = ArgumentSignature::from(args);
self.indices indices
.get(&signature) .get(&signature)
.map(|index| &mut self.functions[*index]) .map(|index| &mut functions[*index])
.ok_or_else(|| FunctionError::NoOverload { .ok_or_else(|| FunctionError::NoOverload {
expected: self.indices.keys().cloned().collect(), expected: indices.keys().cloned().collect(),
received: signature, received: signature,
}) })
}
}
}
/// Returns the function information contained in the map.
pub fn info(&self) -> FunctionInfoType {
match self {
Self::Single(_, info) => FunctionInfoType::Standard(Cow::Borrowed(info)),
Self::Overloaded(_, info, _) => FunctionInfoType::Overloaded(Cow::Borrowed(info)),
} }
} }
@ -60,42 +70,97 @@ impl<F> FunctionMap<F> {
/// ///
/// If the other map contains any functions with the same signature as 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. /// an error will be returned along with the original, unchanged map.
pub fn merge(mut self, other: Self) -> Result<Self, (Box<Self>, FunctionOverloadError)> { pub fn merge(self, other: Self) -> Result<Self, (Box<Self>, FunctionOverloadError)> {
// === Function Map === // match (self, other) {
let mut other_indices = HashMap::new(); (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,
},
));
}
for (sig, index) in other.indices { Ok(Self::Overloaded(
if self.indices.contains_key(&sig) { vec![self_func, other_func],
return Err((Box::new(self), FunctionOverloadError { signature: sig })); vec![self_info, other_info],
HashMap::from([(self_sig, 0), (other_sig, 1)]),
))
} }
(
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,
},
));
}
other_indices.insert(sig, self.functions.len() + index); for index in other_indices.values_mut() {
*index += 1;
}
other_funcs.insert(0, self_func);
other_infos.insert(0, self_info);
other_indices.insert(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(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),
) => {
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(sig, self_funcs.len() + index);
}
self_indices.extend(new_indices);
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))
}
} }
// === 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((Box::new(self), 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).chain(other_infos).collect())
}
FunctionInfoType::Overloaded(infos) => FunctionInfoType::Overloaded(
IntoIterator::into_iter(infos).chain(other_infos).collect(),
),
};
Ok(self)
} }
} }
@ -106,100 +171,166 @@ mod tests {
use crate::Type; use crate::Type;
#[test] #[test]
fn should_merge_function_maps() { fn should_merge_single_into_single() {
let map_a = FunctionMap { let map_a = FunctionMap::Single('a', FunctionInfo::anonymous().with_arg::<i8>("arg0"));
info: FunctionInfoType::Overloaded(Box::new([ let map_b = FunctionMap::Single('b', FunctionInfo::anonymous().with_arg::<u8>("arg0"));
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 { let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
info: FunctionInfoType::Overloaded(Box::new([ panic!("expected overloaded function");
};
assert_eq!(functions, vec!['a', 'b']);
assert_eq!(infos.len(), 2);
assert_eq!(
indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 1),
])
);
}
#[test]
fn should_merge_single_into_overloaded() {
let map_a = FunctionMap::Single('a', FunctionInfo::anonymous().with_arg::<i8>("arg0"));
let map_b = FunctionMap::Overloaded(
vec!['b', 'c'],
vec![
FunctionInfo::anonymous().with_arg::<u8>("arg0"), FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<u16>("arg0"), FunctionInfo::anonymous().with_arg::<u16>("arg0"),
FunctionInfo::anonymous().with_arg::<u32>("arg0"), ],
])), HashMap::from([
functions: vec!['d', 'e', 'f'],
indices: HashMap::from([
(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),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 2),
]), ]),
);
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
}; };
assert_eq!(functions, vec!['a', 'b', 'c']);
let map = map_a.merge(map_b).unwrap(); assert_eq!(infos.len(), 3);
assert_eq!(map.functions, vec!['a', 'b', 'c', 'd', 'e', 'f']);
assert_eq!( assert_eq!(
map.indices, indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 2),
])
);
}
#[test]
fn should_merge_overloaed_into_single() {
let map_a = FunctionMap::Overloaded(
vec!['a', 'b'],
vec![
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
],
HashMap::from([ HashMap::from([
(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),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2), ]),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 3), );
(ArgumentSignature::from_iter([Type::of::<u16>()]), 4), let map_b = FunctionMap::Single('c', FunctionInfo::anonymous().with_arg::<u8>("arg0"));
(ArgumentSignature::from_iter([Type::of::<u32>()]), 5),
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);
assert_eq!(
indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 2),
])
);
}
#[test]
fn should_merge_overloaded_into_overloaded() {
let map_a = FunctionMap::Overloaded(
vec!['a', 'b'],
vec![
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
],
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
]),
);
let map_b = FunctionMap::Overloaded(
vec!['c', 'd'],
vec![
FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<u16>("arg0"),
],
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(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);
assert_eq!(
indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 2),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 3),
]) ])
); );
} }
#[test] #[test]
fn should_return_error_on_duplicate_signature() { fn should_return_error_on_duplicate_signature() {
let map_a = FunctionMap { let map_a = FunctionMap::Single(
info: FunctionInfoType::Overloaded(Box::new([ 'a',
FunctionInfo::anonymous().with_arg::<i8>("arg0"), FunctionInfo::anonymous()
FunctionInfo::anonymous().with_arg::<i16>("arg0"), .with_arg::<i8>("arg0")
FunctionInfo::anonymous().with_arg::<i32>("arg0"), .with_arg::<i16>("arg1"),
])), );
functions: vec!['a', 'b', 'c'], let map_b = FunctionMap::Overloaded(
indices: HashMap::from([ vec!['b', 'c'],
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), vec![
(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::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"), FunctionInfo::anonymous().with_arg::<u16>("arg1"),
FunctionInfo::anonymous().with_arg::<u32>("arg0"), ],
])), HashMap::from([
functions: vec!['d', 'e', 'f'], (
indices: HashMap::from([ ArgumentSignature::from_iter([Type::of::<u8>(), Type::of::<u16>()]),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0), 0,
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1), ),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 2), (
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()]),
1,
),
]), ]),
};
let Err((map_a, error)) = map_a.merge(map_b) else {
panic!("expected an error");
};
assert_eq!(
error,
FunctionOverloadError {
signature: ArgumentSignature::from_iter([Type::of::<i16>()])
}
); );
// Assert that the original map remains unchanged: let (map, error) = map_a.merge(map_b).unwrap_err();
assert_eq!(map_a.functions, vec!['a', 'b', 'c']);
assert_eq!( assert_eq!(
map_a.indices, error.signature,
HashMap::from([ ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0), );
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2), // Assert the original map remains unchanged:
]) let FunctionMap::Single(function, info) = *map else {
panic!("expected single function");
};
assert_eq!(function, 'a');
assert_eq!(
ArgumentSignature::from(info),
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
); );
} }
} }

View file

@ -17,49 +17,51 @@ use crate::{
/// A wrapper around [`FunctionInfo`] used to represent either a standard function /// A wrapper around [`FunctionInfo`] used to represent either a standard function
/// or an overloaded function. /// or an overloaded function.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum FunctionInfoType { pub enum FunctionInfoType<'a> {
/// A standard function with a single set of arguments. /// A standard function with a single set of arguments.
/// ///
/// This includes generic functions with a single set of monomorphized arguments. /// This includes generic functions with a single set of monomorphized arguments.
Standard(FunctionInfo), Standard(Cow<'a, FunctionInfo>),
/// An overloaded function with multiple sets of arguments. /// An overloaded function with multiple sets of arguments.
/// ///
/// This includes generic functions with multiple sets of monomorphized 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"). /// as well as functions with a variable number of arguments (i.e. "variadic functions").
Overloaded(Box<[FunctionInfo]>), Overloaded(Cow<'a, [FunctionInfo]>),
} }
impl From<FunctionInfo> for FunctionInfoType { impl From<FunctionInfo> for FunctionInfoType<'_> {
fn from(info: FunctionInfo) -> Self { fn from(info: FunctionInfo) -> Self {
FunctionInfoType::Standard(info) FunctionInfoType::Standard(Cow::Owned(info))
} }
} }
impl TryFrom<Vec<FunctionInfo>> for FunctionInfoType { impl TryFrom<Vec<FunctionInfo>> for FunctionInfoType<'_> {
type Error = MissingFunctionInfoError; type Error = MissingFunctionInfoError;
fn try_from(mut infos: Vec<FunctionInfo>) -> Result<Self, Self::Error> { fn try_from(mut infos: Vec<FunctionInfo>) -> Result<Self, Self::Error> {
match infos.len() { match infos.len() {
0 => Err(MissingFunctionInfoError), 0 => Err(MissingFunctionInfoError),
1 => Ok(Self::Standard(infos.pop().unwrap())), 1 => Ok(Self::Standard(Cow::Owned(infos.pop().unwrap()))),
_ => Ok(Self::Overloaded(infos.into_boxed_slice())), _ => Ok(Self::Overloaded(Cow::Owned(infos))),
} }
} }
} }
impl IntoIterator for FunctionInfoType { impl IntoIterator for FunctionInfoType<'_> {
type Item = FunctionInfo; type Item = FunctionInfo;
type IntoIter = vec::IntoIter<FunctionInfo>; type IntoIter = vec::IntoIter<FunctionInfo>;
fn into_iter(self) -> Self::IntoIter { 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 { match self {
FunctionInfoType::Standard(info) => vec![info].into_iter(), FunctionInfoType::Standard(info) => vec![info.into_owned()].into_iter(),
FunctionInfoType::Overloaded(infos) => infos.into_vec().into_iter(), FunctionInfoType::Overloaded(infos) => infos.into_owned().into_iter(),
} }
} }
} }
impl FunctionInfoType { impl FunctionInfoType<'_> {
pub fn arg_count(&self) -> usize { pub fn arg_count(&self) -> usize {
match self { match self {
Self::Standard(info) => info.arg_count(), Self::Standard(info) => info.arg_count(),