use std::any::type_name;
use std::fmt;
use std::sync::Arc;
use crate::BytesBuf;
use crate::mem::Memory;
pub struct CallbackMemory<FReserve>
where
FReserve: Fn(usize) -> BytesBuf + Send + Sync + 'static,
{
reserve_fn: Arc<FReserve>,
}
impl<FReserve> CallbackMemory<FReserve>
where
FReserve: Fn(usize) -> BytesBuf + Send + Sync + 'static,
{
pub fn new(reserve_fn: FReserve) -> Self {
Self {
reserve_fn: Arc::new(reserve_fn),
}
}
#[must_use]
pub fn reserve(&self, min_bytes: usize) -> crate::BytesBuf {
(self.reserve_fn)(min_bytes)
}
}
impl<FReserve> Memory for CallbackMemory<FReserve>
where
FReserve: Fn(usize) -> BytesBuf + Send + Sync + 'static,
{
#[cfg_attr(test, mutants::skip)] fn reserve(&self, min_bytes: usize) -> BytesBuf {
self.reserve(min_bytes)
}
}
impl<FReserve> Clone for CallbackMemory<FReserve>
where
FReserve: Fn(usize) -> BytesBuf + Send + Sync + 'static,
{
fn clone(&self) -> Self {
Self {
reserve_fn: Arc::clone(&self.reserve_fn),
}
}
}
impl<FReserve> fmt::Debug for CallbackMemory<FReserve>
where
FReserve: Fn(usize) -> BytesBuf + Send + Sync + 'static,
{
#[cfg_attr(test, mutants::skip)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(type_name::<Self>())
.field("reserve_fn", &"Fn(usize) -> BytesBuf")
.finish()
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use std::sync::atomic::{self, AtomicUsize};
use static_assertions::assert_impl_all;
use super::*;
use crate::mem::MemoryShared;
use crate::mem::testing::TransparentMemory;
assert_impl_all!(CallbackMemory<fn(usize) -> BytesBuf>: MemoryShared);
#[test]
fn calls_back_to_provided_fn() {
let callback_called_times = Arc::new(AtomicUsize::new(0));
let provider = CallbackMemory::new({
let callback_called_times = Arc::clone(&callback_called_times);
move |min_bytes| {
callback_called_times.fetch_add(1, atomic::Ordering::SeqCst);
TransparentMemory::new().reserve(min_bytes)
}
});
_ = Memory::reserve(&provider, 100);
assert_eq!(callback_called_times.load(atomic::Ordering::SeqCst), 1);
}
#[test]
fn clone_shares_underlying_callback() {
let callback_called_times = Arc::new(AtomicUsize::new(0));
let provider = CallbackMemory::new({
let callback_called_times = Arc::clone(&callback_called_times);
move |min_bytes| {
callback_called_times.fetch_add(1, atomic::Ordering::SeqCst);
TransparentMemory::new().reserve(min_bytes)
}
});
let cloned_provider = provider.clone();
_ = Memory::reserve(&provider, 50);
assert_eq!(callback_called_times.load(atomic::Ordering::SeqCst), 1);
_ = Memory::reserve(&cloned_provider, 75);
assert_eq!(callback_called_times.load(atomic::Ordering::SeqCst), 2);
}
#[test]
fn debug_output_contains_type_and_field_info() {
let provider = CallbackMemory::new(|min_bytes| TransparentMemory::new().reserve(min_bytes));
_ = Memory::reserve(&provider, 50);
let debug_output = format!("{provider:?}");
assert!(debug_output.contains("CallbackMemory"), "Debug output should contain type name");
assert!(debug_output.contains("reserve_fn"), "Debug output should contain field name");
assert!(
debug_output.contains("Fn(usize) -> BytesBuf"),
"Debug output should contain function signature description"
);
}
}