mod composite;
mod extractors;
pub use composite::{CompositeKey, CompositeKey3, EitherKey, OptionalKey};
pub use extractors::*;
pub trait Key<R>: Send + Sync + 'static {
fn extract(&self, request: &R) -> Option<String>;
fn name(&self) -> &'static str;
}
#[derive(Debug, Clone, Default)]
pub struct GlobalKey;
impl GlobalKey {
pub fn new() -> Self {
Self
}
}
impl<R> Key<R> for GlobalKey {
fn extract(&self, _request: &R) -> Option<String> {
Some("global".to_string())
}
fn name(&self) -> &'static str {
"global"
}
}
#[derive(Clone)]
pub struct FnKey<F> {
extractor: F,
name: &'static str,
}
impl<F> std::fmt::Debug for FnKey<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FnKey").field("name", &self.name).finish()
}
}
impl<F> FnKey<F> {
pub fn new(name: &'static str, extractor: F) -> Self {
Self { extractor, name }
}
}
impl<R, F> Key<R> for FnKey<F>
where
F: Fn(&R) -> Option<String> + Send + Sync + 'static,
{
fn extract(&self, request: &R) -> Option<String> {
(self.extractor)(request)
}
fn name(&self) -> &'static str {
self.name
}
}
#[derive(Debug, Clone)]
pub struct StaticKey {
key: String,
}
impl StaticKey {
pub fn new(key: impl Into<String>) -> Self {
Self { key: key.into() }
}
}
impl<R> Key<R> for StaticKey {
fn extract(&self, _request: &R) -> Option<String> {
Some(self.key.clone())
}
fn name(&self) -> &'static str {
"static"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_global_key() {
let key = GlobalKey::new();
let request = ();
assert_eq!(Key::<()>::extract(&key, &request), Some("global".to_string()));
assert_eq!(Key::<()>::name(&key), "global");
}
#[test]
fn test_static_key() {
let key = StaticKey::new("my-key");
let request = ();
assert_eq!(key.extract(&request), Some("my-key".to_string()));
}
#[test]
fn test_fn_key() {
let key: FnKey<fn(&i32) -> Option<String>> = FnKey::new("custom", |_: &i32| Some("from-fn".to_string()));
assert_eq!(key.extract(&42), Some("from-fn".to_string()));
assert_eq!(key.name(), "custom");
}
}