use crate::func::args::ArgCount;
use crate::func::signature::{ArgListSignature, ArgumentSignature};
use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionOverloadError};
use alloc::{borrow::Cow, vec, vec::Vec};
use bevy_platform::collections::HashMap;
use core::fmt::{Debug, Formatter};
#[derive(Clone)]
pub(super) struct DynamicFunctionInternal<F> {
functions: Vec<F>,
info: FunctionInfo,
arg_map: HashMap<ArgumentSignature, usize>,
}
impl<F> DynamicFunctionInternal<F> {
pub fn new(func: F, info: FunctionInfo) -> Self {
let arg_map = info
.signatures()
.iter()
.map(|sig| (ArgumentSignature::from(sig), 0))
.collect();
Self {
functions: vec![func],
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
}
pub fn name(&self) -> Option<&Cow<'static, str>> {
self.info.name()
}
pub fn is_overloaded(&self) -> bool {
self.info.is_overloaded()
}
pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> {
if !self.info.is_overloaded() {
return Ok(&self.functions[0]);
}
let signature = ArgListSignature::from(args);
self.arg_map
.get(&signature)
.map(|index| &self.functions[*index])
.ok_or_else(|| FunctionError::NoOverload {
expected: self.arg_map.keys().cloned().collect(),
received: ArgumentSignature::from(args),
})
}
pub fn get_mut(&mut self, args: &ArgList) -> Result<&mut F, FunctionError> {
if !self.info.is_overloaded() {
return Ok(&mut self.functions[0]);
}
let signature = ArgListSignature::from(args);
self.arg_map
.get(&signature)
.map(|index| &mut self.functions[*index])
.ok_or_else(|| FunctionError::NoOverload {
expected: self.arg_map.keys().cloned().collect(),
received: ArgumentSignature::from(args),
})
}
#[inline]
pub fn info(&self) -> &FunctionInfo {
&self.info
}
pub fn arg_count(&self) -> ArgCount {
self.info.arg_count()
}
pub fn validate_args(&self, args: &ArgList) -> Result<(), FunctionError> {
let expected_arg_count = self.arg_count();
let received_arg_count = args.len();
if !expected_arg_count.contains(received_arg_count) {
Err(FunctionError::ArgCountMismatch {
expected: expected_arg_count,
received: received_arg_count,
})
} else {
Ok(())
}
}
pub fn merge(&mut self, mut other: Self) -> Result<(), FunctionOverloadError> {
let mut new_signatures = <HashMap<_, _>>::default();
for (sig, index) in other.arg_map {
if self.arg_map.contains_key(&sig) {
return Err(FunctionOverloadError::DuplicateSignature(sig));
}
new_signatures.insert(sig, self.functions.len() + index);
}
self.arg_map.reserve(new_signatures.len());
for (sig, index) in new_signatures {
self.arg_map.insert(sig, index);
}
self.functions.append(&mut other.functions);
self.info.extend_unchecked(other.info);
Ok(())
}
pub fn map_functions<G>(self, f: fn(F) -> G) -> DynamicFunctionInternal<G> {
DynamicFunctionInternal {
functions: self.functions.into_iter().map(f).collect(),
info: self.info,
arg_map: self.arg_map,
}
}
}
impl<F> Debug for DynamicFunctionInternal<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
self.info
.pretty_printer()
.include_fn_token()
.include_name()
.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::func::{FunctionInfo, SignatureInfo};
use crate::Type;
#[test]
fn should_merge_single_into_single() {
let mut func_a = DynamicFunctionInternal::new(
'a',
FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0")),
);
let func_b = DynamicFunctionInternal::new(
'b',
FunctionInfo::new(SignatureInfo::anonymous().with_arg::<u8>("arg0")),
);
func_a.merge(func_b).unwrap();
assert_eq!(func_a.functions, vec!['a', 'b']);
assert_eq!(func_a.info.signatures().len(), 2);
assert_eq!(
func_a.arg_map,
HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 1),
])
);
}
#[test]
fn should_merge_single_into_overloaded() {
let mut func_a = DynamicFunctionInternal::new(
'a',
FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0")),
);
let func_b = DynamicFunctionInternal {
functions: vec!['b', 'c'],
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::<u16>()]), 1),
]),
};
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!(
func_a.arg_map,
HashMap::from_iter([
(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_overload_into_single() {
let mut func_a = DynamicFunctionInternal {
functions: vec!['a', 'b'],
info: FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0"))
.with_overload(SignatureInfo::anonymous().with_arg::<i16>("arg0"))
.unwrap(),
arg_map: HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
]),
};
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!(
func_a.arg_map,
HashMap::from_iter([
(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 mut func_a = DynamicFunctionInternal {
functions: vec!['a', 'b'],
info: FunctionInfo::new(SignatureInfo::anonymous().with_arg::<i8>("arg0"))
.with_overload(SignatureInfo::anonymous().with_arg::<i16>("arg0"))
.unwrap(),
arg_map: HashMap::from_iter([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
]),
};
let func_b = DynamicFunctionInternal {
functions: vec!['c', 'd'],
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::<u16>()]), 1),
]),
};
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!(
func_a.arg_map,
HashMap::from_iter([
(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 mut func_a = DynamicFunctionInternal::new(
'a',
FunctionInfo::new(
SignatureInfo::anonymous()
.with_arg::<i8>("arg0")
.with_arg::<i16>("arg1"),
),
);
let func_b = DynamicFunctionInternal {
functions: vec!['b', 'c'],
info: FunctionInfo::new(
SignatureInfo::anonymous()
.with_arg::<u8>("arg0")
.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>()]),
0,
),
(
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()]),
1,
),
]),
};
let FunctionOverloadError::DuplicateSignature(duplicate) =
func_a.merge(func_b).unwrap_err()
else {
panic!("Expected `FunctionOverloadError::DuplicateSignature`");
};
assert_eq!(
duplicate,
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
);
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
),])
);
}
}