fire_http_representation/header/
values.rs

1use std::borrow::Cow;
2use std::fmt;
3
4pub use http::header::{
5	AsHeaderName, HeaderName, HeaderValue, IntoHeaderName, InvalidHeaderValue,
6};
7
8#[cfg(feature = "json")]
9pub use serde_json::Error as JsonError;
10
11/// Contains all http header values.
12///
13/// This is really similar to `http::header::HeaderMap` except
14/// that is uses IntoHeaderValue for inserting. And it does not allow
15/// multiples values for a given key.
16#[derive(Debug, Clone)]
17pub struct HeaderValues(http::HeaderMap<HeaderValue>);
18
19impl HeaderValues {
20	/// Creates a new empty `HeaderValues`.
21	pub fn new() -> Self {
22		Self(http::HeaderMap::new())
23	}
24
25	/// Creates a new `HeaderValues` from it's inner type.
26	pub fn from_inner(inner: http::HeaderMap<HeaderValue>) -> Self {
27		Self(inner)
28	}
29
30	/// Insert a new key and value into the header.
31	///
32	/// If a value to this key is already present
33	/// that value is dropped.
34	///
35	/// ## Panics
36	/// If the value is not a valid HeaderValue.
37	pub fn insert<K, V>(&mut self, key: K, val: V) -> Option<HeaderValue>
38	where
39		K: IntoHeaderName,
40		V: TryInto<HeaderValue>,
41		V::Error: fmt::Debug,
42	{
43		let val = val.try_into().expect("invalid HeaderValue");
44		self.0.insert(key, val)
45	}
46
47	/// Insert a new key and value into the header. Returning
48	/// None if the value is not valid.
49	///
50	/// If a value to this key is already present
51	/// that value is dropped.
52	pub fn try_insert<K, V>(
53		&mut self,
54		key: K,
55		val: V,
56	) -> Result<Option<HeaderValue>, InvalidHeaderValue>
57	where
58		K: IntoHeaderName,
59		V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
60	{
61		Ok(self.0.insert(key, val.try_into()?))
62	}
63
64	/// Insert a new key and value into the header. Percent encoding
65	/// the value if necessary.
66	pub fn encode_value<K, V>(&mut self, key: K, val: V) -> Option<HeaderValue>
67	where
68		K: IntoHeaderName,
69		V: IntoEncodedHeaderValue,
70	{
71		let val = val.into_encoded_header_value();
72		self.0.insert(key, val)
73	}
74
75	/// Insert a new key and a serializeable value. The value will be serialized
76	/// as json and percent encoded.
77	///
78	/// Returns `None` if the value could not be serialized or inserted.
79	#[cfg(feature = "json")]
80	#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
81	pub fn serialize_value<K, V: ?Sized>(
82		&mut self,
83		key: K,
84		val: &V,
85	) -> Result<Option<HeaderValue>, JsonError>
86	where
87		K: IntoHeaderName,
88		V: serde::Serialize,
89	{
90		let v = serde_json::to_string(val)?;
91		Ok(self.encode_value(key, v))
92	}
93
94	/// Returns the value if it exists.
95	pub fn get<K>(&self, key: K) -> Option<&HeaderValue>
96	where
97		K: AsHeaderName,
98	{
99		self.0.get(key)
100	}
101
102	/// Returns the value mutably if it exists.
103	pub fn get_mut<K>(&mut self, key: K) -> Option<&mut HeaderValue>
104	where
105		K: AsHeaderName,
106	{
107		self.0.get_mut(key)
108	}
109
110	/// Returns the value as a string if it exists and is valid.
111	pub fn get_str<K>(&self, key: K) -> Option<&str>
112	where
113		K: AsHeaderName,
114	{
115		self.get(key).and_then(|v| v.to_str().ok())
116	}
117
118	/// Returns the value percent decoded as a string if it exists and is valid.
119	pub fn decode_value<K>(
120		&self,
121		key: K,
122	) -> Option<Result<Cow<'_, str>, std::str::Utf8Error>>
123	where
124		K: AsHeaderName,
125	{
126		self.get(key).map(|v| {
127			percent_encoding::percent_decode(v.as_bytes()).decode_utf8()
128		})
129	}
130
131	/// Deserializes a given value. Returning `None` if the value
132	/// does not exist or is not valid json.
133	#[cfg(feature = "json")]
134	#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
135	pub fn deserialize_value<K, D>(
136		&self,
137		key: K,
138	) -> Option<Result<D, serde_json::Error>>
139	where
140		K: AsHeaderName,
141		D: serde::de::DeserializeOwned,
142	{
143		self.get(key).map(|v| {
144			let s = percent_encoding::percent_decode(v.as_bytes())
145				.decode_utf8_lossy();
146			serde_json::from_str(s.as_ref())
147		})
148	}
149
150	/// Returns the inner `HeaderMap`.
151	pub fn into_inner(self) -> http::HeaderMap<HeaderValue> {
152		self.0
153	}
154}
155
156fn encode_to_header_value(s: impl AsRef<[u8]>) -> HeaderValue {
157	let s: String = percent_encoding::percent_encode(
158		s.as_ref(),
159		percent_encoding::CONTROLS,
160	)
161	.collect();
162	// does not allocate again
163	let b: bytes::Bytes = s.into();
164	// now lets make a header value
165	// TODO probably can be changed to
166	// from maybe shared unchecked
167	// but need to check first control covers all cases
168	HeaderValue::from_maybe_shared(b).unwrap()
169}
170
171/// Converts a value into a `HeaderValue` and encodes it if necessary.
172pub trait IntoEncodedHeaderValue {
173	fn into_encoded_header_value(self) -> HeaderValue;
174}
175
176macro_rules! impl_into_header_value {
177	($(
178		$s:ty, $self:ident => $ex:expr
179	),*) => ($(
180		impl IntoEncodedHeaderValue for $s {
181			#[inline]
182			fn into_encoded_header_value($self) -> HeaderValue { $ex }
183		}
184	)*);
185	(REF, $(
186		$s:ty, $self:ident => $ex:expr
187	),*) => ($(
188		impl<'a> IntoEncodedHeaderValue for &'a $s {
189			#[inline]
190			fn into_encoded_header_value($self) -> HeaderValue { $ex }
191		}
192	)*);
193}
194
195impl_into_header_value! {
196	HeaderName, self => self.into(),
197	HeaderValue, self => self,
198	i16, self => self.into(),
199	i32, self => self.into(),
200	i64, self => self.into(),
201	isize, self => self.into(),
202	u16, self => self.into(),
203	u32, self => self.into(),
204	u64, self => self.into(),
205	usize, self => self.into(),
206	String, self => encode_to_header_value(self),
207	Vec<u8>, self => encode_to_header_value(self)
208}
209
210impl_into_header_value! { REF,
211	HeaderValue, self => self.clone(),
212	[u8], self => encode_to_header_value(self),
213	str, self => encode_to_header_value(self)
214}
215
216#[cfg(test)]
217mod tests {
218	#![allow(unused_imports)]
219
220	use super::*;
221	use serde::{Deserialize, Serialize};
222
223	#[test]
224	fn test_encdec() {
225		let mut values = HeaderValues::new();
226		values.encode_value("Rocket", "🚀 Rocket");
227		let s = values.get_str("Rocket").unwrap();
228		assert_eq!(s, "%F0%9F%9A%80 Rocket");
229
230		let s = values.decode_value("Rocket").unwrap().unwrap();
231		assert_eq!(s, "🚀 Rocket");
232	}
233
234	#[cfg(feature = "json")]
235	#[test]
236	fn test_serde() {
237		#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
238		struct Value {
239			text: String,
240			number: usize,
241		}
242
243		let mut values = HeaderValues::new();
244		let val = Value {
245			text: "🚀 Rocket".into(),
246			number: 42,
247		};
248		values.serialize_value("Value", &val).unwrap();
249
250		let s = values.get_str("Value").unwrap();
251		assert_eq!(s, "{\"text\":\"%F0%9F%9A%80 Rocket\",\"number\":42}");
252
253		let n_val: Value = values.deserialize_value("Value").unwrap().unwrap();
254		assert_eq!(n_val, val);
255	}
256}