use axum::extract::FromRequestParts;
use http::request::Parts;
#[derive(Debug, Clone)]
pub struct LastEventId(pub Option<String>);
impl<S> FromRequestParts<S> for LastEventId
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("last-event-id")
.and_then(|v| v.to_str().ok())
.map(String::from);
Ok(LastEventId(value))
}
}
#[cfg(test)]
mod tests {
use super::*;
use axum::extract::FromRequestParts;
use http::Request;
#[tokio::test]
async fn extracts_last_event_id_header() {
let (mut parts, _body) = Request::builder()
.header("last-event-id", "evt_42")
.body(())
.unwrap()
.into_parts();
let result = LastEventId::from_request_parts(&mut parts, &()).await;
let last_id = result.unwrap();
assert_eq!(last_id.0, Some("evt_42".to_string()));
}
#[tokio::test]
async fn returns_none_when_header_absent() {
let (mut parts, _body) = Request::builder().body(()).unwrap().into_parts();
let result = LastEventId::from_request_parts(&mut parts, &()).await;
let last_id = result.unwrap();
assert_eq!(last_id.0, None);
}
#[tokio::test]
async fn non_visible_ascii_header_returns_none() {
let (mut parts, _body) = Request::builder().body(()).unwrap().into_parts();
parts.headers.insert(
"last-event-id",
http::HeaderValue::from_bytes(&[0x80]).unwrap(),
);
let result = LastEventId::from_request_parts(&mut parts, &()).await;
let last_id = result.unwrap();
assert_eq!(last_id.0, None);
}
}