chuchi_core/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>(
82		&mut self,
83		key: K,
84		val: &V,
85	) -> Result<Option<HeaderValue>, JsonError>
86	where
87		K: IntoHeaderName,
88		V: serde::Serialize + ?Sized,
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
156impl Default for HeaderValues {
157	fn default() -> Self {
158		Self::new()
159	}
160}
161
162fn encode_to_header_value(s: impl AsRef<[u8]>) -> HeaderValue {
163	let s: String = percent_encoding::percent_encode(
164		s.as_ref(),
165		percent_encoding::CONTROLS,
166	)
167	.collect();
168	// does not allocate again
169	let b: bytes::Bytes = s.into();
170	// now lets make a header value
171	// TODO probably can be changed to
172	// from maybe shared unchecked
173	// but need to check first control covers all cases
174	HeaderValue::from_maybe_shared(b).unwrap()
175}
176
177/// Converts a value into a `HeaderValue` and encodes it if necessary.
178pub trait IntoEncodedHeaderValue {
179	fn into_encoded_header_value(self) -> HeaderValue;
180}
181
182macro_rules! impl_into_header_value {
183	($(
184		$s:ty, $self:ident => $ex:expr
185	),*) => ($(
186		impl IntoEncodedHeaderValue for $s {
187			#[inline]
188			fn into_encoded_header_value($self) -> HeaderValue { $ex }
189		}
190	)*);
191	(REF, $(
192		$s:ty, $self:ident => $ex:expr
193	),*) => ($(
194		impl<'a> IntoEncodedHeaderValue for &'a $s {
195			#[inline]
196			fn into_encoded_header_value($self) -> HeaderValue { $ex }
197		}
198	)*);
199}
200
201impl_into_header_value! {
202	HeaderName, self => self.into(),
203	HeaderValue, self => self,
204	i16, self => self.into(),
205	i32, self => self.into(),
206	i64, self => self.into(),
207	isize, self => self.into(),
208	u16, self => self.into(),
209	u32, self => self.into(),
210	u64, self => self.into(),
211	usize, self => self.into(),
212	String, self => encode_to_header_value(self),
213	Vec<u8>, self => encode_to_header_value(self)
214}
215
216impl_into_header_value! { REF,
217	HeaderValue, self => self.clone(),
218	[u8], self => encode_to_header_value(self),
219	str, self => encode_to_header_value(self)
220}
221
222#[cfg(test)]
223mod tests {
224	#![allow(unused_imports)]
225
226	use super::*;
227	use serde::{Deserialize, Serialize};
228
229	#[test]
230	fn test_encdec() {
231		let mut values = HeaderValues::new();
232		values.encode_value("Rocket", "🚀 Rocket");
233		let s = values.get_str("Rocket").unwrap();
234		assert_eq!(s, "%F0%9F%9A%80 Rocket");
235
236		let s = values.decode_value("Rocket").unwrap().unwrap();
237		assert_eq!(s, "🚀 Rocket");
238	}
239
240	#[cfg(feature = "json")]
241	#[test]
242	fn test_serde() {
243		#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
244		struct Value {
245			text: String,
246			number: usize,
247		}
248
249		let mut values = HeaderValues::new();
250		let val = Value {
251			text: "🚀 Rocket".into(),
252			number: 42,
253		};
254		values.serialize_value("Value", &val).unwrap();
255
256		let s = values.get_str("Value").unwrap();
257		assert_eq!(s, "{\"text\":\"%F0%9F%9A%80 Rocket\",\"number\":42}");
258
259		let n_val: Value = values.deserialize_value("Value").unwrap().unwrap();
260		assert_eq!(n_val, val);
261	}
262}