tocket 0.2.1

A implemetation of 'Token Bucket' algorithm
Documentation
use crate::{RateLimitExceededError, State, Storage, TokenBucketAlgorithm};

/// A storage that stores state in memory.
///
/// Useful for single application instance or for tests.
///
/// # Example
/// ```
/// # fn main() {
/// use tocket::{TokenBucket, InMemoryStorage};
///
/// fn main() {
///     let tb = TokenBucket::new(InMemoryStorage::new(2));
///     assert!(tb.try_acquire(2).is_ok());
///     assert!(tb.try_acquire_one().is_err());
/// }
/// # }
/// ```
pub struct InMemoryStorage {
    state: parking_lot::Mutex<State>,
}

impl InMemoryStorage {
    /// Creates a storage.
    pub fn new(rps_limit: u32) -> Self {
        Self {
            state: parking_lot::Mutex::new(State {
                cap: rps_limit,
                available_tokens: rps_limit,
                last_refill: time::OffsetDateTime::now_utc(),
                refill_tick: time::Duration::seconds(1) / rps_limit,
            }),
        }
    }
}

impl Storage for InMemoryStorage {
    type Error = RateLimitExceededError;

    fn try_acquire(&self, alg: TokenBucketAlgorithm, permits: u32) -> Result<(), Self::Error> {
        let mut state = self.state.lock();
        alg.try_acquire(&mut state, permits)?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::TokenBucket;

    use std::time::Duration;

    #[test]
    fn try_acquire() {
        let tb = TokenBucket::new(InMemoryStorage::new(2));
        assert!(tb.try_acquire(2).is_ok());
        assert!(tb.try_acquire_one().is_err());

        std::thread::sleep(Duration::from_secs(1));
        assert!(tb.try_acquire(2).is_ok());
        assert!(tb.try_acquire_one().is_err());

        std::thread::sleep(Duration::from_secs(1));
        assert!(tb.try_acquire(2).is_ok());
        assert!(tb.try_acquire_one().is_err());
    }
}