1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use ;
use Request;
use Layer;
use Service;
/// Extension type inserted into request extensions by [`tier_cost`].
///
/// `TierLimitService` reads this to determine the cost of the current request.
/// If absent, the cost comes from
/// [`TierLimitLayer::cost_fn`](crate::TierLimitLayer::cost_fn), or is 1.
;
/// Create a Tower layer that sets the rate limit cost for requests passing through it.
///
/// A cost of `0` means the request does not consume any quota — useful for
/// health checks, metrics endpoints, or OPTIONS preflight requests. The user
/// is still identified and tracked, but no tokens are deducted.
///
/// If no `tier_cost` layer is applied, the cost comes from
/// [`TierLimitLayer::cost_fn`](crate::TierLimitLayer::cost_fn), or is `1`.
///
/// The cost is read when the request reaches the rate limiter, so this layer
/// must wrap the limiter (run before it). In axum, a route's own `.layer()`
/// runs *inside* a limiter added with `Router::layer`, so the cost would
/// arrive too late; use `cost_fn` there, or apply both layers to the route
/// with `tier_cost` added last.
///
/// # Examples
///
/// ```rust,no_run
/// use std::convert::Infallible;
///
/// use axum::{Router, routing::post};
/// use tower_rate_tier::{tier_cost, Quota, RateTier, TierLimitLayer};
///
/// let limiter = TierLimitLayer::new(RateTier::builder().tier("free", Quota::per_hour(100)).build());
///
/// // On a method router the last `.layer()` runs first: tier_cost, then the limiter.
/// let app: Router = Router::new().route(
/// "/export",
/// post(|| async { "ok" })
/// .layer::<_, Infallible>(limiter)
/// .layer::<_, Infallible>(tier_cost(20)), // 20 tokens
/// );
/// ```
/// Layer that inserts [`TierCost`] into request extensions.
;
/// Service that inserts [`TierCost`] into request extensions before forwarding.