tower_rate_limiter/limiter/
policy.rs1use std::time::Duration;
10
11use http::Request;
12
13use super::{error::RateLimitError, response::RateLimitFields, store::Usage};
14
15#[non_exhaustive]
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct Policy {
19 pub name: String,
21 pub limit: u64,
23 pub window: Duration,
25 pub used: u64,
27 pub reset_after: Duration,
29}
30
31impl Policy {
32 pub(super) fn from_usage(name: String, window: Duration, limit: u64, usage: Usage) -> Result<Self, RateLimitError> {
34 if usage.used == 0 {
35 return Err(RateLimitError::Store(
36 String::from("invalid_usage"),
37 String::from("rate-limit store returned zero usage"),
38 ));
39 }
40
41 Ok(Self {
42 name,
43 limit,
44 window,
45 used: usage.used,
46 reset_after: usage.reset_after,
47 })
48 }
49
50 pub fn remaining(&self) -> u64 {
52 self.limit.saturating_sub(self.used)
53 }
54
55 pub fn is_rate_limited(&self) -> bool {
57 self.used > self.limit
58 }
59}
60
61#[derive(Clone, Debug, Default, PartialEq, Eq)]
63pub struct RateLimitContext {
64 policies: Vec<Policy>,
66}
67
68impl RateLimitContext {
69 pub const fn new() -> Self {
71 Self { policies: Vec::new() }
72 }
73
74 pub fn policies(&self) -> &[Policy] {
76 &self.policies
77 }
78}
79
80#[derive(Clone, Debug)]
82pub(super) struct ResponseMetadata {
83 pub(super) policy: Policy,
84 pub(super) fields: RateLimitFields,
85}
86
87impl ResponseMetadata {
88 pub(super) fn new(policy: Policy, fields: RateLimitFields) -> Self {
89 Self { policy, fields }
90 }
91}
92
93pub(super) fn make_key(policy_name: &str, client_key: &str) -> String {
95 format!("{}:{}", escape_key_part(policy_name), escape_key_part(client_key))
96}
97
98fn escape_key_part(value: &str) -> String {
99 value.replace('%', "%25").replace(':', "%3A")
100}
101
102pub(super) fn append_context<B>(request: &mut Request<B>, metadata: &ResponseMetadata) {
104 request
105 .extensions_mut()
106 .get_or_insert_default::<RateLimitContext>()
107 .policies
108 .push(metadata.policy.clone());
109}