type TokenCount = u32;
pub type RealTimeClock = std::time::SystemTime;
pub trait Clock: Send + Sync + 'static {
fn now() -> u32;
}
impl Clock for RealTimeClock {
fn now() -> u32 {
RealTimeClock::now()
.duration_since(RealTimeClock::UNIX_EPOCH)
.unwrap()
.as_secs() as u32
}
}
pub struct GenericTokenBucket(TokenCount);
impl GenericTokenBucket {
const MAX_TOKENS: u32 = 100;
const TOKENS_PER_SECOND: u32 = 2;
pub const fn new() -> Self {
Self(0)
}
fn get_tokens_with_time(&self, now: u32) -> TokenCount {
std::cmp::max(self.0, now - Self::MAX_TOKENS / Self::TOKENS_PER_SECOND)
}
fn get_tokens<T: Clock>(&mut self) -> TokenCount {
self.get_tokens_with_time(T::now())
}
pub fn check<T: Clock>(&self, tokens: u32) -> bool {
let now = T::now();
let cur_tokens = self.get_tokens_with_time(now);
let avail_tokens = (now as i64 - cur_tokens as i64) * (Self::TOKENS_PER_SECOND as i64);
tokens as i64 <= avail_tokens
}
pub fn deplete<T: Clock>(&mut self, tokens: u32) {
self.0 = self.get_tokens::<T>() + tokens.div_ceil(Self::TOKENS_PER_SECOND);
}
#[allow(dead_code)]
pub fn refill<T: Clock>(&mut self, tokens: u32) {
self.0 = self.get_tokens::<T>() - tokens.div_ceil(Self::TOKENS_PER_SECOND);
}
#[allow(dead_code)]
pub fn empty<T: Clock>(&mut self) {
self.0 = T::now();
}
}
impl Default for GenericTokenBucket {
fn default() -> Self {
Self::new()
}
}
#[test]
fn test_tokens() {
let mut bucket = GenericTokenBucket::new();
bucket.empty::<RealTimeClock>();
bucket.refill::<RealTimeClock>(20);
assert!(bucket.check::<RealTimeClock>(10));
bucket.deplete::<RealTimeClock>(40);
assert!(!bucket.check::<RealTimeClock>(10));
}