Skip to main content

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}