1use std::ops::{Deref, DerefMut};
2
3use boluo_core::extract::{FromRequest, OptionalFromRequest};
4use boluo_core::http::HeaderName;
5use boluo_core::request::Request;
6use headers::{Header, HeaderMapExt};
7
8#[derive(Debug, Clone, Copy)]
24pub struct TypedHeader<T>(pub T);
25
26impl<T> Deref for TypedHeader<T> {
27 type Target = T;
28
29 #[inline]
30 fn deref(&self) -> &Self::Target {
31 &self.0
32 }
33}
34
35impl<T> DerefMut for TypedHeader<T> {
36 #[inline]
37 fn deref_mut(&mut self) -> &mut Self::Target {
38 &mut self.0
39 }
40}
41
42impl<T> TypedHeader<T> {
43 #[inline]
45 pub fn into_inner(this: Self) -> T {
46 this.0
47 }
48}
49
50impl<T> FromRequest for TypedHeader<T>
51where
52 T: Header,
53{
54 type Error = TypedHeaderExtractError;
55
56 async fn from_request(req: &mut Request) -> Result<Self, Self::Error> {
57 Option::<TypedHeader<T>>::from_request(req)
58 .await?
59 .ok_or_else(|| TypedHeaderExtractError::MissingHeader { name: T::name() })
60 }
61}
62
63impl<T> OptionalFromRequest for TypedHeader<T>
64where
65 T: Header,
66{
67 type Error = TypedHeaderExtractError;
68
69 async fn from_request(req: &mut Request) -> Result<Option<Self>, Self::Error> {
70 req.headers()
71 .typed_try_get()
72 .map(|v| v.map(TypedHeader))
73 .map_err(|source| TypedHeaderExtractError::ParseError {
74 name: T::name(),
75 source,
76 })
77 }
78}
79
80#[derive(Debug)]
82pub enum TypedHeaderExtractError {
83 MissingHeader {
85 name: &'static HeaderName,
87 },
88 ParseError {
90 name: &'static HeaderName,
92 source: headers::Error,
94 },
95}
96
97impl std::fmt::Display for TypedHeaderExtractError {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 match self {
100 TypedHeaderExtractError::MissingHeader { name } => {
101 write!(f, "missing request header `{name}`")
102 }
103 TypedHeaderExtractError::ParseError { name, source } => {
104 write!(f, "failed to parse request header `{name}` ({source})")
105 }
106 }
107 }
108}
109
110impl std::error::Error for TypedHeaderExtractError {}