use std::borrow::Cow;
use std::fmt;
pub use http::header::{
AsHeaderName, HeaderName, HeaderValue, IntoHeaderName, InvalidHeaderValue,
};
#[cfg(feature = "json")]
pub use serde_json::Error as JsonError;
#[derive(Debug, Clone)]
pub struct HeaderValues(http::HeaderMap<HeaderValue>);
impl HeaderValues {
pub fn new() -> Self {
Self(http::HeaderMap::new())
}
pub fn from_inner(inner: http::HeaderMap<HeaderValue>) -> Self {
Self(inner)
}
pub fn insert<K, V>(&mut self, key: K, val: V) -> Option<HeaderValue>
where
K: IntoHeaderName,
V: TryInto<HeaderValue>,
V::Error: fmt::Debug,
{
let val = val.try_into().expect("invalid HeaderValue");
self.0.insert(key, val)
}
pub fn try_insert<K, V>(
&mut self,
key: K,
val: V,
) -> Result<Option<HeaderValue>, InvalidHeaderValue>
where
K: IntoHeaderName,
V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
{
Ok(self.0.insert(key, val.try_into()?))
}
pub fn encode_value<K, V>(&mut self, key: K, val: V) -> Option<HeaderValue>
where
K: IntoHeaderName,
V: IntoEncodedHeaderValue,
{
let val = val.into_encoded_header_value();
self.0.insert(key, val)
}
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn serialize_value<K, V: ?Sized>(
&mut self,
key: K,
val: &V,
) -> Result<Option<HeaderValue>, JsonError>
where
K: IntoHeaderName,
V: serde::Serialize,
{
let v = serde_json::to_string(val)?;
Ok(self.encode_value(key, v))
}
pub fn get<K>(&self, key: K) -> Option<&HeaderValue>
where
K: AsHeaderName,
{
self.0.get(key)
}
pub fn get_mut<K>(&mut self, key: K) -> Option<&mut HeaderValue>
where
K: AsHeaderName,
{
self.0.get_mut(key)
}
pub fn get_str<K>(&self, key: K) -> Option<&str>
where
K: AsHeaderName,
{
self.get(key).and_then(|v| v.to_str().ok())
}
pub fn decode_value<K>(&self, key: K) -> Option<Cow<'_, str>>
where
K: AsHeaderName,
{
self.get(key).and_then(|v| {
percent_encoding::percent_decode(v.as_bytes())
.decode_utf8()
.ok()
})
}
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub fn deserialize_value<K, D>(&self, key: K) -> Option<D>
where
K: AsHeaderName,
D: serde::de::DeserializeOwned,
{
let v = self.decode_value(key)?;
serde_json::from_str(v.as_ref()).ok()
}
pub fn into_inner(self) -> http::HeaderMap<HeaderValue> {
self.0
}
}
fn encode_to_header_value(s: impl AsRef<[u8]>) -> HeaderValue {
let s: String = percent_encoding::percent_encode(
s.as_ref(),
percent_encoding::CONTROLS,
)
.collect();
let b: bytes::Bytes = s.into();
HeaderValue::from_maybe_shared(b).unwrap()
}
pub trait IntoEncodedHeaderValue {
fn into_encoded_header_value(self) -> HeaderValue;
}
macro_rules! impl_into_header_value {
($(
$s:ty, $self:ident => $ex:expr
),*) => ($(
impl IntoEncodedHeaderValue for $s {
#[inline]
fn into_encoded_header_value($self) -> HeaderValue { $ex }
}
)*);
(REF, $(
$s:ty, $self:ident => $ex:expr
),*) => ($(
impl<'a> IntoEncodedHeaderValue for &'a $s {
#[inline]
fn into_encoded_header_value($self) -> HeaderValue { $ex }
}
)*);
}
impl_into_header_value! {
HeaderName, self => self.into(),
HeaderValue, self => self,
i16, self => self.into(),
i32, self => self.into(),
i64, self => self.into(),
isize, self => self.into(),
u16, self => self.into(),
u32, self => self.into(),
u64, self => self.into(),
usize, self => self.into(),
String, self => encode_to_header_value(self),
Vec<u8>, self => encode_to_header_value(self)
}
impl_into_header_value! { REF,
HeaderValue, self => self.clone(),
[u8], self => encode_to_header_value(self),
str, self => encode_to_header_value(self)
}
#[cfg(test)]
mod tests {
#![allow(unused_imports)]
use super::*;
use serde::{Deserialize, Serialize};
#[test]
fn test_encdec() {
let mut values = HeaderValues::new();
values.encode_value("Rocket", "🚀 Rocket");
let s = values.get_str("Rocket").unwrap();
assert_eq!(s, "%F0%9F%9A%80 Rocket");
let s = values.decode_value("Rocket").unwrap();
assert_eq!(s, "🚀 Rocket");
}
#[cfg(feature = "json")]
#[test]
fn test_serde() {
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Value {
text: String,
number: usize,
}
let mut values = HeaderValues::new();
let val = Value {
text: "🚀 Rocket".into(),
number: 42,
};
values.serialize_value("Value", &val).unwrap();
let s = values.get_str("Value").unwrap();
assert_eq!(s, "{\"text\":\"%F0%9F%9A%80 Rocket\",\"number\":42}");
let n_val: Value = values.deserialize_value("Value").unwrap();
assert_eq!(n_val, val);
}
}