use alloc::borrow::Cow;
use core::fmt::{Debug, Formatter};
use crate::func::{
args::ArgList, info::FunctionInfo, DynamicFunction, FunctionError, FunctionResult,
IntoFunctionMut,
};
pub struct DynamicFunctionMut<'env> {
info: FunctionInfo,
func: Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>,
}
impl<'env> DynamicFunctionMut<'env> {
pub fn new<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>(
func: F,
info: FunctionInfo,
) -> Self {
Self {
info,
func: Box::new(func),
}
}
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.info = self.info.with_name(name);
self
}
pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> {
let expected_arg_count = self.info.arg_count();
let received_arg_count = args.len();
if expected_arg_count != received_arg_count {
Err(FunctionError::ArgCountMismatch {
expected: expected_arg_count,
received: received_arg_count,
})
} else {
(self.func)(args)
}
}
pub fn call_once(mut self, args: ArgList) -> FunctionResult {
let expected_arg_count = self.info.arg_count();
let received_arg_count = args.len();
if expected_arg_count != received_arg_count {
Err(FunctionError::ArgCountMismatch {
expected: expected_arg_count,
received: received_arg_count,
})
} else {
(self.func)(args)
}
}
pub fn info(&self) -> &FunctionInfo {
&self.info
}
pub fn name(&self) -> Option<&Cow<'static, str>> {
self.info.name()
}
}
impl<'env> Debug for DynamicFunctionMut<'env> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let name = self.info.name().unwrap_or(&Cow::Borrowed("_"));
write!(f, "DynamicFunctionMut(fn {name}(")?;
for (index, arg) in self.info.args().iter().enumerate() {
let name = arg.name().unwrap_or("_");
let ty = arg.type_path();
write!(f, "{name}: {ty}")?;
if index + 1 < self.info.args().len() {
write!(f, ", ")?;
}
}
let ret = self.info.return_info().type_path();
write!(f, ") -> {ret})")
}
}
impl<'env> From<DynamicFunction<'env>> for DynamicFunctionMut<'env> {
#[inline]
fn from(function: DynamicFunction<'env>) -> Self {
Self {
info: function.info,
func: Box::new(move |args| (function.func)(args)),
}
}
}
impl<'env> IntoFunctionMut<'env, ()> for DynamicFunctionMut<'env> {
#[inline]
fn into_function_mut(self) -> DynamicFunctionMut<'env> {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_overwrite_function_name() {
let mut total = 0;
let func = (|a: i32, b: i32| total = a + b)
.into_function_mut()
.with_name("my_function");
assert_eq!(func.info().name().unwrap(), "my_function");
}
#[test]
fn should_convert_dynamic_function_mut_with_into_function() {
fn make_closure<'env, F: IntoFunctionMut<'env, M>, M>(f: F) -> DynamicFunctionMut<'env> {
f.into_function_mut()
}
let mut total = 0;
let closure: DynamicFunctionMut = make_closure(|a: i32, b: i32| total = a + b);
let _: DynamicFunctionMut = make_closure(closure);
}
#[test]
fn should_return_error_on_arg_count_mismatch() {
let mut total = 0;
let mut func = (|a: i32, b: i32| total = a + b).into_function_mut();
let args = ArgList::default().push_owned(25_i32);
let error = func.call(args).unwrap_err();
assert!(matches!(
error,
FunctionError::ArgCountMismatch {
expected: 2,
received: 1
}
));
let args = ArgList::default().push_owned(25_i32);
let error = func.call_once(args).unwrap_err();
assert!(matches!(
error,
FunctionError::ArgCountMismatch {
expected: 2,
received: 1
}
));
}
}