1use redis::{Cmd as RedisCmd, ToRedisArgs};
2use std::time::Duration;
3
4#[derive(Clone, Copy, Debug)]
5#[non_exhaustive]
6pub struct Policy {
7 pub burst: usize,
8
9 pub tokens: usize,
10
11 pub period: Duration,
12
13 pub apply: usize,
14
15 pub name: Option<&'static str>,
16}
17
18impl Policy {
19 pub const fn new(burst: usize, tokens: usize, period: Duration, apply: usize) -> Policy {
20 Self {
21 burst,
22 tokens,
23 period,
24 apply,
25 name: None,
26 }
27 }
28
29 pub const fn from_tokens_per_second(tokens: usize) -> Policy {
30 Policy::from_tokens_per_period(tokens, Duration::from_secs(1))
31 }
32
33 pub const fn from_tokens_per_minute(tokens: usize) -> Policy {
34 Policy::from_tokens_per_period(tokens, Duration::from_secs(60))
35 }
36
37 pub const fn from_tokens_per_hour(tokens: usize) -> Policy {
38 Policy::from_tokens_per_period(tokens, Duration::from_secs(60 * 60))
39 }
40
41 pub const fn from_tokens_per_day(tokens: usize) -> Policy {
42 Policy::from_tokens_per_period(tokens, Duration::from_secs(60 * 60 * 24))
43 }
44
45 pub const fn from_tokens_per_period(tokens: usize, period: Duration) -> Policy {
46 Policy::new(0, tokens, period, 1)
47 }
48
49 pub const fn max_burst(mut self, burst: usize) -> Policy {
50 self.burst = burst;
51 self
52 }
53
54 pub const fn apply_tokens(mut self, apply: usize) -> Policy {
55 self.apply = apply;
56 self
57 }
58
59 pub const fn name(mut self, name: &'static str) -> Policy {
60 self.name = Some(name);
61 self
62 }
63}
64
65pub struct Cmd<'a, K> {
66 key: K,
67 policy: &'a Policy,
68}
69
70impl<'a, K> Cmd<'a, K> {
71 pub fn new(key: K, policy: &'a Policy) -> Self {
72 Cmd { key, policy }
73 }
74}
75
76impl<'a, K> From<Cmd<'a, K>> for RedisCmd
77where
78 K: ToRedisArgs,
79{
80 fn from(Cmd { key, policy }: Cmd<'a, K>) -> Self {
81 let mut cmd = RedisCmd::new();
82 cmd.arg("CL.THROTTLE")
83 .arg(key)
84 .arg(policy.burst)
85 .arg(policy.tokens)
86 .arg(policy.period.as_secs())
87 .arg(policy.apply);
88 cmd
89 }
90}