hitbox_http/extractors/method.rs
1use async_trait::async_trait;
2use hitbox::{Extractor, KeyPart, KeyParts};
3
4use super::NeutralExtractor;
5use crate::CacheableHttpRequest;
6
7/// Extracts the HTTP method as a cache key part.
8///
9/// Adds a key part with name `"method"` and the method as value (e.g., `"GET"`, `"POST"`).
10/// Use this as the starting point for extractor chains.
11///
12/// # Type Parameters
13///
14/// * `E` - The inner extractor to chain with. Use [`Method::new`] to start
15/// a new extractor chain (uses [`NeutralExtractor`] internally), or use the
16/// [`MethodExtractor`] extension trait to chain onto an existing extractor.
17///
18/// # Examples
19///
20/// ```
21/// use hitbox_http::extractors::{Method, path::PathExtractor, query::QueryExtractor};
22///
23/// # use bytes::Bytes;
24/// # use http_body_util::Empty;
25/// # use hitbox_http::extractors::{NeutralExtractor, Path, query::Query};
26/// let extractor = Method::new()
27/// .path("/users/{user_id}")
28/// .query("page".to_string());
29/// # let _: &Query<Path<Method<NeutralExtractor<Empty<Bytes>>>>> = &extractor;
30/// ```
31///
32/// # Key Parts Generated
33///
34/// Generates a single key part: `method={METHOD}` where `{METHOD}` is the
35/// uppercase HTTP method name (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, etc.).
36#[derive(Debug)]
37pub struct Method<E> {
38 inner: E,
39}
40
41impl<S> Method<NeutralExtractor<S>> {
42 /// Creates a method extractor as the starting point for cache key generation.
43 ///
44 /// Adds a key part with name `"method"` and the HTTP method as value
45 /// (e.g., `"GET"`, `"POST"`). Chain additional extractors to build
46 /// a complete cache key.
47 ///
48 /// Chain onto existing extractors using [`MethodExtractor::method`] instead
49 /// if you already have an extractor chain.
50 pub fn new() -> Self {
51 Self {
52 inner: NeutralExtractor::new(),
53 }
54 }
55}
56
57impl<S> Default for Method<NeutralExtractor<S>> {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63/// Extension trait for adding method extraction to an extractor chain.
64///
65/// # For Callers
66///
67/// Chain this after [`Method::new()`] or any other extractor to add the HTTP
68/// method to your cache key. The method is added as a key part with name
69/// `"method"` and value like `"GET"` or `"POST"`.
70///
71/// # For Implementors
72///
73/// This trait is automatically implemented for all [`Extractor`]
74/// types. You don't need to implement it manually.
75pub trait MethodExtractor: Sized {
76 /// Adds HTTP method extraction to the chain.
77 fn method(self) -> Method<Self>;
78}
79
80impl<E> MethodExtractor for E
81where
82 E: Extractor,
83{
84 fn method(self) -> Method<Self> {
85 Method { inner: self }
86 }
87}
88
89#[async_trait]
90impl<ReqBody, E> Extractor for Method<E>
91where
92 ReqBody: hyper::body::Body + Send + 'static,
93 ReqBody::Error: Send,
94 E: Extractor<Subject = CacheableHttpRequest<ReqBody>> + Send + Sync,
95{
96 type Subject = E::Subject;
97
98 async fn get(&self, subject: Self::Subject) -> KeyParts<Self::Subject> {
99 let method = subject.parts().method.to_string();
100 let mut parts = self.inner.get(subject).await;
101 parts.push(KeyPart::new("method", Some(method)));
102 parts
103 }
104}