use super::{Cache, SegmentedCache};
use std::{
collections::hash_map::RandomState,
hash::{BuildHasher, Hash},
marker::PhantomData,
time::Duration,
};
pub struct CacheBuilder<C> {
max_capacity: usize,
initial_capacity: Option<usize>,
num_segments: Option<usize>,
time_to_live: Option<Duration>,
time_to_idle: Option<Duration>,
cache_type: PhantomData<C>,
}
impl<K, V> CacheBuilder<Cache<K, V, RandomState>>
where
K: Eq + Hash,
V: Clone,
{
pub fn new(max_capacity: usize) -> Self {
Self {
max_capacity,
initial_capacity: None,
num_segments: None,
time_to_live: None,
time_to_idle: None,
cache_type: PhantomData::default(),
}
}
pub fn segments(self, num_segments: usize) -> CacheBuilder<SegmentedCache<K, V, RandomState>> {
assert!(num_segments > 1);
CacheBuilder {
max_capacity: self.max_capacity,
initial_capacity: self.initial_capacity,
num_segments: Some(num_segments),
time_to_live: self.time_to_live,
time_to_idle: self.time_to_idle,
cache_type: PhantomData::default(),
}
}
pub fn build(self) -> Cache<K, V, RandomState> {
let build_hasher = RandomState::default();
Cache::with_everything(
self.max_capacity,
self.initial_capacity,
build_hasher,
self.time_to_live,
self.time_to_idle,
)
}
pub fn build_with_hasher<S>(self, hasher: S) -> Cache<K, V, S>
where
S: BuildHasher + Clone,
{
Cache::with_everything(
self.max_capacity,
self.initial_capacity,
hasher,
self.time_to_live,
self.time_to_idle,
)
}
}
impl<K, V> CacheBuilder<SegmentedCache<K, V, RandomState>>
where
K: Eq + Hash,
V: Clone,
{
pub fn build(self) -> SegmentedCache<K, V, RandomState> {
let build_hasher = RandomState::default();
SegmentedCache::with_everything(
self.max_capacity,
self.initial_capacity,
self.num_segments.unwrap(),
build_hasher,
self.time_to_live,
self.time_to_idle,
)
}
pub fn build_with_hasher<S>(self, hasher: S) -> SegmentedCache<K, V, S>
where
S: BuildHasher + Clone,
{
SegmentedCache::with_everything(
self.max_capacity,
self.initial_capacity,
self.num_segments.unwrap(),
hasher,
self.time_to_live,
self.time_to_idle,
)
}
}
impl<C> CacheBuilder<C> {
pub fn initial_capacity(self, capacity: usize) -> Self {
Self {
initial_capacity: Some(capacity),
..self
}
}
pub fn time_to_live(self, duration: Duration) -> Self {
Self {
time_to_live: Some(duration),
..self
}
}
pub fn time_to_idle(self, duration: Duration) -> Self {
Self {
time_to_idle: Some(duration),
..self
}
}
}
#[cfg(test)]
mod tests {
use super::CacheBuilder;
use std::time::Duration;
#[test]
fn build_cache() {
let cache = CacheBuilder::new(100).build();
assert_eq!(cache.max_capacity(), 100);
assert_eq!(cache.time_to_live(), None);
assert_eq!(cache.time_to_idle(), None);
assert_eq!(cache.num_segments(), 1);
cache.insert('a', "Alice");
assert_eq!(cache.get(&'a'), Some("Alice"));
let cache = CacheBuilder::new(100)
.time_to_live(Duration::from_secs(45 * 60))
.time_to_idle(Duration::from_secs(15 * 60))
.build();
assert_eq!(cache.max_capacity(), 100);
assert_eq!(cache.time_to_live(), Some(Duration::from_secs(45 * 60)));
assert_eq!(cache.time_to_idle(), Some(Duration::from_secs(15 * 60)));
assert_eq!(cache.num_segments(), 1);
cache.insert('a', "Alice");
assert_eq!(cache.get(&'a'), Some("Alice"));
}
#[test]
fn build_segmented_cache() {
let cache = CacheBuilder::new(100).segments(16).build();
assert_eq!(cache.max_capacity(), 100);
assert_eq!(cache.time_to_live(), None);
assert_eq!(cache.time_to_idle(), None);
assert_eq!(cache.num_segments(), 16_usize.next_power_of_two());
cache.insert('b', "Bob");
assert_eq!(cache.get(&'b'), Some("Bob"));
let cache = CacheBuilder::new(100)
.segments(16)
.time_to_live(Duration::from_secs(45 * 60))
.time_to_idle(Duration::from_secs(15 * 60))
.build();
assert_eq!(cache.max_capacity(), 100);
assert_eq!(cache.time_to_live(), Some(Duration::from_secs(45 * 60)));
assert_eq!(cache.time_to_idle(), Some(Duration::from_secs(15 * 60)));
assert_eq!(cache.num_segments(), 16_usize.next_power_of_two());
cache.insert('b', "Bob");
assert_eq!(cache.get(&'b'), Some("Bob"));
}
}