From e6be82157fe718570aa13ab12803ee39083b8dff Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Mon, 16 Sep 2024 11:15:23 -0400 Subject: Some code cleanup on events --- src/header.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/header.rs (limited to 'src/header.rs') diff --git a/src/header.rs b/src/header.rs new file mode 100644 index 0000000..904e29d --- /dev/null +++ b/src/header.rs @@ -0,0 +1,58 @@ +use axum::{ + extract::FromRequestParts, + http::{request::Parts, HeaderName, HeaderValue}, +}; +use axum_extra::typed_header::TypedHeader; + +/// A typed header. When used as a bare extractor, reads from the +/// `Last-Event-Id` HTTP header. +pub struct LastEventId(pub String); + +static LAST_EVENT_ID: HeaderName = HeaderName::from_static("last-event-id"); + +impl headers::Header for LastEventId { + fn name() -> &'static HeaderName { + &LAST_EVENT_ID + } + + fn decode<'i, I>(values: &mut I) -> Result + where + I: Iterator, + { + let value = values.next().ok_or_else(headers::Error::invalid)?; + if let Ok(value) = value.to_str() { + Ok(Self(value.into())) + } else { + Err(headers::Error::invalid()) + } + } + + fn encode(&self, values: &mut E) + where + E: Extend, + { + let Self(value) = self; + // Must panic or suppress; the trait provides no other options. + let value = HeaderValue::from_str(value).expect("LastEventId is a valid header value"); + + values.extend(std::iter::once(value)); + } +} + +#[async_trait::async_trait] +impl FromRequestParts for LastEventId +where + S: Send + Sync, +{ + type Rejection = as FromRequestParts>::Rejection; + + async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { + // This is purely for ergonomics: it allows `RequestedAt` to be extracted + // without having to wrap it in `Extension<>`. Callers _can_ still do that, + // but they aren't forced to. + let TypedHeader(requested_at) = + TypedHeader::::from_request_parts(parts, state).await?; + + Ok(requested_at) + } +} -- cgit v1.2.3