1#![deny(clippy::all)]
13#![deny(clippy::cargo)]
14#![deny(clippy::pedantic)]
15#![deny(clippy::print_stdout)]
16#![deny(clippy::print_stderr)]
17#![deny(clippy::dbg_macro)]
18#![deny(unused_extern_crates)]
19#![deny(unused_allocation)]
20#![deny(unused_assignments)]
21#![deny(unused_comparisons)]
22#![deny(unreachable_pub)]
23#![deny(missing_docs)]
24#![deny(missing_copy_implementations)]
25#![deny(missing_debug_implementations)]
26#![allow(clippy::cast_precision_loss)]
27#![allow(clippy::multiple_crate_versions)]
28
29use async_trait::async_trait;
30use serde::{Deserialize, Serialize};
31use std::num::NonZeroU32;
32use tokio::time::{self, Duration, Instant};
33
34pub mod stable;
35
36#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
37#[serde(rename_all = "snake_case")]
39pub enum Config {
40 AllOut,
42 Stable,
44}
45
46impl Default for Config {
47 fn default() -> Self {
48 Self::Stable
49 }
50}
51
52#[derive(thiserror::Error, Debug, Clone, Copy)]
54pub enum Error {
55 #[error(transparent)]
57 Stable(#[from] stable::Error),
58}
59
60#[async_trait]
61pub trait Clock {
63 fn ticks_elapsed(&self) -> u64;
65 async fn wait(&self, ticks: u64);
67}
68
69#[derive(Debug, Clone, Copy)]
70pub struct RealClock {
72 start: Instant,
73}
74
75impl Default for RealClock {
76 fn default() -> Self {
77 Self {
78 start: Instant::now(),
79 }
80 }
81}
82
83#[async_trait]
84impl Clock for RealClock {
85 #[allow(clippy::cast_possible_truncation)]
91 fn ticks_elapsed(&self) -> u64 {
92 let now = Instant::now();
93 let ticks_since: u128 = now.duration_since(self.start).as_micros();
94 assert!(
95 ticks_since <= u128::from(u64::MAX),
96 "584,554 years elapsed since last call!"
97 );
98 ticks_since as u64
99 }
100
101 async fn wait(&self, ticks: u64) {
102 time::sleep(Duration::from_micros(ticks)).await;
103 }
104}
105
106#[derive(Debug)]
108pub enum Throttle<C = RealClock> {
109 Stable(stable::Stable<C>),
111 AllOut,
114}
115
116impl Throttle<RealClock> {
117 #[must_use]
119 pub fn new_with_config(config: Config, maximum_capacity: NonZeroU32) -> Self {
120 match config {
121 Config::Stable => Throttle::Stable(stable::Stable::with_clock(
122 maximum_capacity,
123 RealClock::default(),
124 )),
125 Config::AllOut => Throttle::AllOut,
126 }
127 }
128}
129
130impl<C> Throttle<C>
131where
132 C: Clock + Sync + Send,
133{
134 #[inline]
141 pub async fn wait(&mut self) -> Result<(), Error> {
142 match self {
143 Throttle::Stable(inner) => inner.wait().await?,
144 Throttle::AllOut => (),
145 }
146
147 Ok(())
148 }
149
150 #[inline]
156 pub async fn wait_for(&mut self, request: NonZeroU32) -> Result<(), Error> {
157 match self {
158 Throttle::Stable(inner) => inner.wait_for(request).await?,
159 Throttle::AllOut => (),
160 }
161
162 Ok(())
163 }
164}