1mod composite;
26mod extractors;
27
28pub use composite::{CompositeKey, CompositeKey3, EitherKey, OptionalKey};
29pub use extractors::*;
30
31pub trait Key<R>: Send + Sync + 'static {
41 fn extract(&self, request: &R) -> Option<String>;
46
47 fn name(&self) -> &'static str;
49}
50
51#[derive(Debug, Clone, Default)]
53pub struct GlobalKey;
54
55impl GlobalKey {
56 pub fn new() -> Self {
58 Self
59 }
60}
61
62impl<R> Key<R> for GlobalKey {
63 fn extract(&self, _request: &R) -> Option<String> {
64 Some("global".to_string())
65 }
66
67 fn name(&self) -> &'static str {
68 "global"
69 }
70}
71
72#[derive(Clone)]
76pub struct FnKey<F> {
77 extractor: F,
78 name: &'static str,
79}
80
81impl<F> std::fmt::Debug for FnKey<F> {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 f.debug_struct("FnKey").field("name", &self.name).finish()
84 }
85}
86
87impl<F> FnKey<F> {
88 pub fn new(name: &'static str, extractor: F) -> Self {
90 Self { extractor, name }
91 }
92}
93
94impl<R, F> Key<R> for FnKey<F>
95where
96 F: Fn(&R) -> Option<String> + Send + Sync + 'static,
97{
98 fn extract(&self, request: &R) -> Option<String> {
99 (self.extractor)(request)
100 }
101
102 fn name(&self) -> &'static str {
103 self.name
104 }
105}
106
107#[derive(Debug, Clone)]
109pub struct StaticKey {
110 key: String,
111}
112
113impl StaticKey {
114 pub fn new(key: impl Into<String>) -> Self {
116 Self { key: key.into() }
117 }
118}
119
120impl<R> Key<R> for StaticKey {
121 fn extract(&self, _request: &R) -> Option<String> {
122 Some(self.key.clone())
123 }
124
125 fn name(&self) -> &'static str {
126 "static"
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_global_key() {
136 let key = GlobalKey::new();
137 let request = ();
138 assert_eq!(Key::<()>::extract(&key, &request), Some("global".to_string()));
139 assert_eq!(Key::<()>::name(&key), "global");
140 }
141
142 #[test]
143 fn test_static_key() {
144 let key = StaticKey::new("my-key");
145 let request = ();
146 assert_eq!(key.extract(&request), Some("my-key".to_string()));
147 }
148
149 #[test]
150 fn test_fn_key() {
151 let key: FnKey<fn(&i32) -> Option<String>> = FnKey::new("custom", |_: &i32| Some("from-fn".to_string()));
152 assert_eq!(key.extract(&42), Some("from-fn".to_string()));
153 assert_eq!(key.name(), "custom");
154 }
155}