mirror of
https://github.com/bevyengine/bevy
synced 2025-01-03 16:58:56 +00:00
Optimized FunctionMap
This commit is contained in:
parent
b3b6671d9c
commit
5923cfa0ec
5 changed files with 309 additions and 190 deletions
crates/bevy_reflect/src/func
|
@ -12,7 +12,6 @@ use crate::{
|
|||
};
|
||||
use alloc::{borrow::Cow, boxed::Box, sync::Arc};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use bevy_utils::HashMap;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
|
@ -90,7 +89,7 @@ impl<'env> DynamicFunction<'env> {
|
|||
/// [function overloading]: Self::with_overload
|
||||
pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>(
|
||||
func: F,
|
||||
info: impl TryInto<FunctionInfoType, Error: Debug>,
|
||||
info: impl TryInto<FunctionInfoType<'static>, Error: Debug>,
|
||||
) -> Self {
|
||||
let info = info.try_into().unwrap();
|
||||
|
||||
|
@ -102,19 +101,14 @@ impl<'env> DynamicFunction<'env> {
|
|||
FunctionInfoType::Overloaded(_) => None,
|
||||
},
|
||||
function_map: match info {
|
||||
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
|
||||
FunctionInfoType::Standard(info) => FunctionMap::Single(func, info.into_owned()),
|
||||
FunctionInfoType::Overloaded(infos) => {
|
||||
let indices = infos
|
||||
.iter()
|
||||
.map(|info| (ArgumentSignature::from(info), 0))
|
||||
.collect(),
|
||||
info: FunctionInfoType::Overloaded(infos),
|
||||
},
|
||||
.collect();
|
||||
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.
|
||||
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();
|
||||
|
||||
if matches!(self.function_map.info, FunctionInfoType::Standard(_))
|
||||
if matches!(self.function_map.info(), FunctionInfoType::Standard(_))
|
||||
&& expected_arg_count != received_arg_count
|
||||
{
|
||||
Err(FunctionError::ArgCountMismatch {
|
||||
|
@ -319,8 +313,8 @@ impl<'env> DynamicFunction<'env> {
|
|||
}
|
||||
|
||||
/// Returns the function info.
|
||||
pub fn info(&self) -> &FunctionInfoType {
|
||||
&self.function_map.info
|
||||
pub fn info(&self) -> FunctionInfoType {
|
||||
self.function_map.info()
|
||||
}
|
||||
|
||||
/// The name of the function.
|
||||
|
@ -347,8 +341,8 @@ impl Function for DynamicFunction<'static> {
|
|||
self.name.as_ref()
|
||||
}
|
||||
|
||||
fn info(&self) -> &FunctionInfoType {
|
||||
&self.function_map.info
|
||||
fn info(&self) -> FunctionInfoType {
|
||||
self.function_map.info()
|
||||
}
|
||||
|
||||
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {
|
||||
|
|
|
@ -8,7 +8,6 @@ use crate::func::{
|
|||
args::ArgList, function_map::FunctionMap, signature::ArgumentSignature, DynamicFunction,
|
||||
FunctionError, FunctionInfoType, FunctionOverloadError, FunctionResult, IntoFunctionMut,
|
||||
};
|
||||
use bevy_utils::HashMap;
|
||||
|
||||
/// A [`Box`] containing a callback to a reflected function.
|
||||
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
|
||||
pub fn new<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>(
|
||||
func: F,
|
||||
info: impl TryInto<FunctionInfoType, Error: Debug>,
|
||||
info: impl TryInto<FunctionInfoType<'static>, Error: Debug>,
|
||||
) -> Self {
|
||||
let info = info.try_into().unwrap();
|
||||
|
||||
|
@ -105,19 +104,14 @@ impl<'env> DynamicFunctionMut<'env> {
|
|||
FunctionInfoType::Overloaded(_) => None,
|
||||
},
|
||||
function_map: match info {
|
||||
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
|
||||
FunctionInfoType::Standard(info) => FunctionMap::Single(func, info.into_owned()),
|
||||
FunctionInfoType::Overloaded(infos) => {
|
||||
let indices = infos
|
||||
.iter()
|
||||
.map(|info| (ArgumentSignature::from(info), 0))
|
||||
.collect(),
|
||||
info: FunctionInfoType::Overloaded(infos),
|
||||
},
|
||||
.collect();
|
||||
FunctionMap::Overloaded(vec![func], infos.into_owned(), indices)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -272,10 +266,10 @@ impl<'env> DynamicFunctionMut<'env> {
|
|||
///
|
||||
/// [`call_once`]: DynamicFunctionMut::call_once
|
||||
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();
|
||||
|
||||
if matches!(self.function_map.info, FunctionInfoType::Standard(_))
|
||||
if matches!(self.function_map.info(), FunctionInfoType::Standard(_))
|
||||
&& expected_arg_count != received_arg_count
|
||||
{
|
||||
Err(FunctionError::ArgCountMismatch {
|
||||
|
@ -321,8 +315,8 @@ impl<'env> DynamicFunctionMut<'env> {
|
|||
}
|
||||
|
||||
/// Returns the function info.
|
||||
pub fn info(&self) -> &FunctionInfoType {
|
||||
&self.function_map.info
|
||||
pub fn info(&self) -> FunctionInfoType {
|
||||
self.function_map.info()
|
||||
}
|
||||
|
||||
/// The name of the function.
|
||||
|
@ -378,15 +372,13 @@ impl<'env> From<DynamicFunction<'env>> for DynamicFunctionMut<'env> {
|
|||
fn from(function: DynamicFunction<'env>) -> Self {
|
||||
Self {
|
||||
name: function.name,
|
||||
function_map: FunctionMap {
|
||||
info: function.function_map.info,
|
||||
indices: function.function_map.indices,
|
||||
functions: function
|
||||
.function_map
|
||||
.functions
|
||||
.into_iter()
|
||||
.map(arc_to_box)
|
||||
.collect(),
|
||||
function_map: match function.function_map {
|
||||
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,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ pub trait Function: PartialReflect + Debug {
|
|||
}
|
||||
|
||||
/// The [`FunctionInfoType`] for this function.
|
||||
fn info(&self) -> &FunctionInfoType;
|
||||
fn info(&self) -> FunctionInfoType;
|
||||
|
||||
/// Call this function with the given arguments.
|
||||
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
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;
|
||||
|
||||
/// A helper type for storing a mapping of overloaded functions
|
||||
/// along with the corresponding [function information].
|
||||
///
|
||||
/// [function information]: FunctionInfoType
|
||||
/// [function information]: FunctionInfo
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct FunctionMap<F> {
|
||||
pub info: FunctionInfoType,
|
||||
pub functions: Vec<F>,
|
||||
pub indices: HashMap<ArgumentSignature, usize>,
|
||||
pub(super) enum FunctionMap<F> {
|
||||
Single(F, FunctionInfo),
|
||||
Overloaded(Vec<F>, Vec<FunctionInfo>, HashMap<ArgumentSignature, usize>),
|
||||
}
|
||||
|
||||
impl<F> FunctionMap<F> {
|
||||
|
@ -21,17 +21,18 @@ impl<F> FunctionMap<F> {
|
|||
///
|
||||
/// If no overload matches the provided arguments, an error will be returned.
|
||||
pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> {
|
||||
if self.functions.len() == 1 {
|
||||
Ok(&self.functions[0])
|
||||
} else {
|
||||
let signature = ArgumentSignature::from(args);
|
||||
self.indices
|
||||
.get(&signature)
|
||||
.map(|index| &self.functions[*index])
|
||||
.ok_or_else(|| FunctionError::NoOverload {
|
||||
expected: self.indices.keys().cloned().collect(),
|
||||
received: signature,
|
||||
})
|
||||
match self {
|
||||
Self::Single(function, _) => Ok(function),
|
||||
Self::Overloaded(functions, _, indices) => {
|
||||
let signature = ArgumentSignature::from(args);
|
||||
indices
|
||||
.get(&signature)
|
||||
.map(|index| &functions[*index])
|
||||
.ok_or_else(|| FunctionError::NoOverload {
|
||||
expected: indices.keys().cloned().collect(),
|
||||
received: signature,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,17 +43,26 @@ impl<F> FunctionMap<F> {
|
|||
///
|
||||
/// 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,
|
||||
})
|
||||
match self {
|
||||
Self::Single(function, _) => Ok(function),
|
||||
Self::Overloaded(functions, _, indices) => {
|
||||
let signature = ArgumentSignature::from(args);
|
||||
indices
|
||||
.get(&signature)
|
||||
.map(|index| &mut functions[*index])
|
||||
.ok_or_else(|| FunctionError::NoOverload {
|
||||
expected: indices.keys().cloned().collect(),
|
||||
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,
|
||||
/// an error will be returned along with the original, unchanged map.
|
||||
pub fn merge(mut self, other: Self) -> Result<Self, (Box<Self>, FunctionOverloadError)> {
|
||||
// === Function Map === //
|
||||
let mut other_indices = HashMap::new();
|
||||
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,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
for (sig, index) in other.indices {
|
||||
if self.indices.contains_key(&sig) {
|
||||
return Err((Box::new(self), FunctionOverloadError { signature: sig }));
|
||||
Ok(Self::Overloaded(
|
||||
vec![self_func, other_func],
|
||||
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;
|
||||
|
||||
#[test]
|
||||
fn should_merge_function_maps() {
|
||||
let 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),
|
||||
]),
|
||||
};
|
||||
fn should_merge_single_into_single() {
|
||||
let map_a = FunctionMap::Single('a', FunctionInfo::anonymous().with_arg::<i8>("arg0"));
|
||||
let map_b = FunctionMap::Single('b', FunctionInfo::anonymous().with_arg::<u8>("arg0"));
|
||||
|
||||
let map_b = FunctionMap {
|
||||
info: FunctionInfoType::Overloaded(Box::new([
|
||||
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
|
||||
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::<u16>("arg0"),
|
||||
FunctionInfo::anonymous().with_arg::<u32>("arg0"),
|
||||
])),
|
||||
functions: vec!['d', 'e', 'f'],
|
||||
indices: HashMap::from([
|
||||
],
|
||||
HashMap::from([
|
||||
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
|
||||
(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");
|
||||
};
|
||||
|
||||
let map = map_a.merge(map_b).unwrap();
|
||||
|
||||
assert_eq!(map.functions, vec!['a', 'b', 'c', 'd', 'e', 'f']);
|
||||
assert_eq!(functions, vec!['a', 'b', 'c']);
|
||||
assert_eq!(infos.len(), 3);
|
||||
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([
|
||||
(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),
|
||||
]),
|
||||
);
|
||||
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);
|
||||
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]
|
||||
fn should_return_error_on_duplicate_signature() {
|
||||
let 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([
|
||||
let map_a = FunctionMap::Single(
|
||||
'a',
|
||||
FunctionInfo::anonymous()
|
||||
.with_arg::<i8>("arg0")
|
||||
.with_arg::<i16>("arg1"),
|
||||
);
|
||||
let map_b = FunctionMap::Overloaded(
|
||||
vec!['b', 'c'],
|
||||
vec![
|
||||
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),
|
||||
FunctionInfo::anonymous().with_arg::<u16>("arg1"),
|
||||
],
|
||||
HashMap::from([
|
||||
(
|
||||
ArgumentSignature::from_iter([Type::of::<u8>(), Type::of::<u16>()]),
|
||||
0,
|
||||
),
|
||||
(
|
||||
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:
|
||||
assert_eq!(map_a.functions, vec!['a', 'b', 'c']);
|
||||
let (map, error) = map_a.merge(map_b).unwrap_err();
|
||||
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),
|
||||
])
|
||||
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');
|
||||
assert_eq!(
|
||||
ArgumentSignature::from(info),
|
||||
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,49 +17,51 @@ use crate::{
|
|||
/// A wrapper around [`FunctionInfo`] used to represent either a standard function
|
||||
/// or an overloaded function.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FunctionInfoType {
|
||||
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(FunctionInfo),
|
||||
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(Box<[FunctionInfo]>),
|
||||
Overloaded(Cow<'a, [FunctionInfo]>),
|
||||
}
|
||||
|
||||
impl From<FunctionInfo> for FunctionInfoType {
|
||||
impl From<FunctionInfo> for FunctionInfoType<'_> {
|
||||
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;
|
||||
|
||||
fn try_from(mut infos: Vec<FunctionInfo>) -> Result<Self, Self::Error> {
|
||||
match infos.len() {
|
||||
0 => Err(MissingFunctionInfoError),
|
||||
1 => Ok(Self::Standard(infos.pop().unwrap())),
|
||||
_ => Ok(Self::Overloaded(infos.into_boxed_slice())),
|
||||
1 => Ok(Self::Standard(Cow::Owned(infos.pop().unwrap()))),
|
||||
_ => Ok(Self::Overloaded(Cow::Owned(infos))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for FunctionInfoType {
|
||||
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_iter(),
|
||||
FunctionInfoType::Overloaded(infos) => infos.into_vec().into_iter(),
|
||||
FunctionInfoType::Standard(info) => vec![info.into_owned()].into_iter(),
|
||||
FunctionInfoType::Overloaded(infos) => infos.into_owned().into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionInfoType {
|
||||
impl FunctionInfoType<'_> {
|
||||
pub fn arg_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Standard(info) => info.arg_count(),
|
||||
|
|
Loading…
Reference in a new issue