larust_http/request.rs
1//! Laravel's `Illuminate\Http\Request`, scoped to what it's for here:
2//! reading headers without a handler having to know axum's `HeaderMap`
3//! API. Route/query parameters deliberately stay on their own
4//! `Path<T>`/`Query<T>` extractors rather than being folded into this
5//! type - Laravel's untyped `$request->route('id')` is exactly what
6//! `Path<T>`'s compile-time-typed, auto-rejecting alternative already
7//! replaces the need for (see `routes/api.rs`'s doc comment).
8
9use axum::extract::FromRequestParts;
10use axum::http::request::Parts;
11use axum::http::HeaderMap;
12use std::collections::BTreeMap;
13use std::convert::Infallible;
14
15pub struct Request {
16 headers: HeaderMap,
17}
18
19impl Request {
20 /// Laravel's `$request->header('X-Foo')` - `None` if the header is
21 /// absent *or* isn't valid UTF-8 (Laravel's own string-typed `header()`
22 /// would choke on that case too, just later and less clearly).
23 pub fn header(&self, name: &str) -> Option<&str> {
24 self.headers.get(name)?.to_str().ok()
25 }
26
27 /// Laravel's `$request->headers->all()`, flattened to strings the same
28 /// way `header()` is - a non-UTF-8 value falls back to a placeholder
29 /// instead of failing the whole map.
30 pub fn headers(&self) -> BTreeMap<String, String> {
31 self.headers
32 .iter()
33 .map(|(name, value)| {
34 let value = value.to_str().unwrap_or("<non-utf8>").to_string();
35 (name.to_string(), value)
36 })
37 .collect()
38 }
39}
40
41// GOTCHAS.md: axum-core declares `FromRequestParts` via `#[async_trait]`,
42// not native async-fn-in-traits - an impl written as a plain `async fn`
43// fails with a confusing E0195 lifetime error instead of a clear message
44// about the mismatch.
45#[axum::async_trait]
46impl<S> FromRequestParts<S> for Request
47where
48 S: Send + Sync,
49{
50 type Rejection = Infallible;
51
52 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
53 Ok(Request {
54 headers: parts.headers.clone(),
55 })
56 }
57}