Skip to main content

hitbox_http/predicates/header/
operation.rs

1use http::{HeaderMap, HeaderName, HeaderValue};
2use regex::Regex;
3
4/// Matching operations for HTTP headers.
5///
6/// These operations can be used with both request and response headers.
7///
8/// # Variants
9///
10/// - [`Eq`](Self::Eq): Exact match on header value
11/// - [`Exist`](Self::Exist): Header presence check (any value)
12/// - [`In`](Self::In): Match any of several values
13/// - [`Contains`](Self::Contains): Substring match within header value
14/// - [`Regex`](Self::Regex): Pattern match using regular expression
15///
16/// # Examples
17///
18/// ```
19/// use hitbox_http::predicates::header::Operation;
20/// use http::header::CONTENT_TYPE;
21///
22/// // Match exact header value
23/// let op = Operation::Eq(
24///     CONTENT_TYPE,
25///     "application/json".parse().unwrap(),
26/// );
27///
28/// // Check header exists
29/// let op = Operation::Exist(CONTENT_TYPE);
30///
31/// // Match substring in header value
32/// let op = Operation::Contains(CONTENT_TYPE, "json".to_string());
33/// ```
34///
35/// Using regex for complex patterns:
36///
37/// ```
38/// use hitbox_http::predicates::header::Operation;
39/// use http::header::ACCEPT;
40/// use regex::Regex;
41///
42/// // Match Accept headers containing version info
43/// let op = Operation::Regex(
44///     ACCEPT,
45///     Regex::new(r"application/vnd\.api\+json; version=\d+").unwrap(),
46/// );
47/// ```
48#[derive(Debug)]
49pub enum Operation {
50    /// Use when you need an exact match on a known header value.
51    ///
52    /// Best for specific content types, authorization schemes, or cache directives.
53    Eq(HeaderName, HeaderValue),
54    /// Use when presence of a header determines cacheability, regardless of value.
55    ///
56    /// Best for checking optional headers like `Authorization` or custom API headers.
57    Exist(HeaderName),
58    /// Use when any of several values should trigger caching.
59    ///
60    /// Best for allowing multiple content types or API versions.
61    In(HeaderName, Vec<HeaderValue>),
62    /// Use when matching partial values in complex headers.
63    ///
64    /// Best for content types with parameters (e.g., `"json"` in `application/json; charset=utf-8`).
65    Contains(HeaderName, String),
66    /// Use when header values follow a pattern.
67    ///
68    /// Best for version strings, custom formats, or extracting structured data.
69    Regex(HeaderName, Regex),
70}
71
72impl Operation {
73    /// Check if the operation matches the headers.
74    pub fn check(&self, headers: &HeaderMap) -> bool {
75        match self {
76            Operation::Eq(name, value) => headers
77                .get_all(name)
78                .iter()
79                .any(|header_value| value.eq(header_value)),
80            Operation::Exist(name) => headers.get(name).is_some(),
81            Operation::In(name, values) => headers
82                .get_all(name)
83                .iter()
84                .any(|header_value| values.iter().any(|v| v.eq(header_value))),
85            Operation::Contains(name, substring) => {
86                headers.get_all(name).iter().any(|header_value| {
87                    header_value
88                        .to_str()
89                        .map(|s| s.contains(substring.as_str()))
90                        .unwrap_or(false)
91                })
92            }
93            Operation::Regex(name, regex) => headers.get_all(name).iter().any(|header_value| {
94                header_value
95                    .to_str()
96                    .map(|s| regex.is_match(s))
97                    .unwrap_or(false)
98            }),
99        }
100    }
101}