use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use axum::http::{HeaderValue, StatusCode, header};
use axum::response::{IntoResponse, Response};
pub struct IfNoneMatch(pub Option<String>);
impl<S> FromRequestParts<S> for IfNoneMatch
where
S: Send + Sync,
{
type Rejection = std::convert::Infallible;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let value = parts
.headers
.get(header::IF_NONE_MATCH)
.and_then(|v| v.to_str().ok())
.map(ToOwned::to_owned);
Ok(Self(value))
}
}
#[expect(
clippy::expect_used,
reason = "ETag value is from trusted internal source"
)]
pub fn conditional(etag: &str, if_none_match: &IfNoneMatch) -> Option<Response> {
let client_etag = if_none_match.0.as_deref()?;
if client_etag == etag {
let mut response = StatusCode::NOT_MODIFIED.into_response();
response.headers_mut().insert(
header::ETAG,
HeaderValue::from_str(etag).expect("valid ETag header value"),
);
Some(response)
} else {
None
}
}
#[expect(
clippy::expect_used,
reason = "ETag value is from trusted internal source"
)]
pub fn with_etag<T: IntoResponse>(response: T, etag: &str) -> Response {
let mut response = response.into_response();
response.headers_mut().insert(
header::ETAG,
HeaderValue::from_str(etag).expect("valid ETag header value"),
);
response
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn conditional_returns_304_on_match() {
let inm = IfNoneMatch(Some("\"abc123\"".to_owned()));
let resp = conditional("\"abc123\"", &inm);
assert!(resp.is_some());
let resp = resp.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);
assert_eq!(
resp.headers().get(header::ETAG).unwrap().to_str().unwrap(),
"\"abc123\""
);
}
#[test]
fn conditional_returns_none_on_mismatch() {
let inm = IfNoneMatch(Some("\"old\"".to_owned()));
assert!(conditional("\"new\"", &inm).is_none());
}
#[test]
fn conditional_returns_none_when_header_absent() {
let inm = IfNoneMatch(None);
assert!(conditional("\"abc\"", &inm).is_none());
}
#[test]
fn with_etag_sets_header() {
let resp = with_etag("hello", "\"tag1\"");
assert_eq!(
resp.headers().get(header::ETAG).unwrap().to_str().unwrap(),
"\"tag1\""
);
}
}