openapi_context/
header.rs

1use chrono::{DateTime, Utc};
2use headers::{HeaderValue, HeaderName, Header, HeaderMapExt};
3use lazy_static::lazy_static;
4use std::convert::TryFrom;
5use std::fmt;
6use std::ops::Deref;
7use uuid::Uuid;
8
9/// Header - `x-span-id` - used to track a request through a chain of microservices.
10pub const X_SPAN_ID: &str = "x-span-id";
11
12lazy_static! {
13    pub static ref X_SPAN_ID_HEADER: HeaderName = HeaderName::from_static(X_SPAN_ID);
14}
15
16/// Wrapper for a string being used as an x-span-id.
17#[derive(Debug, Clone)]
18pub struct XSpanId(pub String);
19
20impl XSpanId {
21    /// Extract an x-span-id from a request header if present, and if not
22    /// generate a new one.
23    pub fn get_or_generate<T>(req: &hyper::Request<T>) -> Self {
24        let x_span_id = req.headers().typed_get::<XSpanId>();
25
26        match x_span_id {
27            Some(ref x) => x.clone(),
28            None => Self::default(),
29        }
30    }
31}
32
33impl Header for XSpanId {
34    fn name() -> &'static HeaderName {
35        &X_SPAN_ID_HEADER
36    }
37
38    fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
39        where
40            I: Iterator<Item = &'i HeaderValue>,
41    {
42        let value = values
43            .next()
44            .ok_or_else(headers::Error::invalid)?;
45
46        let value = value.to_str().map_err(|_| headers::Error::invalid())?;
47        Ok(XSpanId(value.to_owned()))
48    }
49
50    fn encode<E>(&self, values: &mut E)
51        where
52            E: Extend<HeaderValue>,
53    {
54        let value = HeaderValue::from_str(&self.0.to_string()).unwrap();
55
56        values.extend(std::iter::once(value));
57    }
58}
59
60impl Default for XSpanId {
61    fn default() -> Self {
62        XSpanId(Uuid::new_v4().to_string())
63    }
64}
65
66impl fmt::Display for XSpanId {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        write!(f, "{}", self.0)
69    }
70}
71
72/// A struct to allow homogeneous conversion into a HeaderValue. We can't
73/// implement the From/Into trait on HeaderValue because we don't own
74/// either of the types.
75#[derive(Debug, Clone)]
76pub struct IntoHeaderValue<T>(pub T);
77
78// Generic implementations
79
80impl<T> Deref for IntoHeaderValue<T> {
81    type Target = T;
82
83    fn deref(&self) -> &T {
84        &self.0
85    }
86}
87
88// Derive for each From<T> in hyper::header::HeaderValue
89
90macro_rules! ihv_generate {
91    ($t:ident) => {
92        impl std::convert::TryFrom<HeaderValue> for IntoHeaderValue<$t> {
93            type Error = headers::Error;
94            fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
95                let value = hdr_value.to_str().map_err(|_| headers::Error::invalid())?;
96                let value = value.parse().map_err(|_| headers::Error::invalid())?;
97                Ok(IntoHeaderValue(value))
98            }
99        }
100
101        impl Into<HeaderValue> for IntoHeaderValue<$t> {
102            fn into(self) -> HeaderValue {
103                HeaderValue::from_str(&self.0.to_string()).unwrap()
104            }
105        }
106    };
107}
108
109ihv_generate!(u64);
110ihv_generate!(i64);
111ihv_generate!(i16);
112ihv_generate!(u16);
113ihv_generate!(u32);
114ihv_generate!(usize);
115ihv_generate!(isize);
116ihv_generate!(i32);
117
118// Custom derivations
119
120impl TryFrom<HeaderValue> for IntoHeaderValue<Vec<String>> {
121    type Error = headers::Error;
122    fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
123        Ok(IntoHeaderValue(
124            hdr_value
125                .to_str()
126                .map_err(|_| headers::Error::invalid())?
127                .split(',')
128                .filter_map(|x| match x.trim() {
129                    "" => None,
130                    y => Some(y.to_string()),
131                })
132                .collect(),
133        ))
134    }
135}
136
137impl Into<HeaderValue> for IntoHeaderValue<Vec<String>> {
138    fn into(self) -> HeaderValue {
139        HeaderValue::from_str(&self.0.join(", ")).unwrap()
140    }
141}
142
143impl TryFrom<HeaderValue> for IntoHeaderValue<String> {
144    type Error = headers::Error;
145    fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
146        let v = hdr_value
147            .to_str()
148            .map_err(|_| headers::Error::invalid())?
149            .to_string();
150        Ok(IntoHeaderValue(v))
151    }
152}
153
154impl Into<HeaderValue> for IntoHeaderValue<String> {
155    fn into(self) -> HeaderValue {
156        HeaderValue::from_str(&self.0).unwrap()
157    }
158}
159
160impl TryFrom<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
161    type Error = headers::Error;
162    fn try_from(hdr_value: HeaderValue) -> Result<Self, Self::Error> {
163        let v = hdr_value.to_str().map_err(|_| headers::Error::invalid())?;
164        Ok(IntoHeaderValue(
165            DateTime::parse_from_rfc3339(v)
166                .map_err(|_| headers::Error::invalid())?
167                .with_timezone(&Utc)
168        ))
169    }
170}
171
172impl Into<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
173    fn into(self) -> HeaderValue {
174        HeaderValue::from_str(self.0.to_rfc3339().as_str()).unwrap()
175    }
176}