1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
use std::fmt;
use std::borrow::Cow;

pub use http::header::{
	HeaderValue, HeaderName, AsHeaderName, IntoHeaderName, InvalidHeaderValue
};

#[cfg(feature = "json")]
pub use serde_json::Error as JsonError;


/// Contains all http header values.
/// 
/// This is really similar to `http::header::HeaderMap` except
/// that is uses IntoHeaderValue for inserting. And it does not allow
/// multiples values for a given key.
#[derive(Debug, Clone)]
pub struct HeaderValues(http::HeaderMap<HeaderValue>);

impl HeaderValues {
	/// Creates a new empty `HeaderValues`.
	pub fn new() -> Self {
		Self(http::HeaderMap::new())
	}

	/// Creates a new `HeaderValues` from it's inner type.
	pub fn from_inner(inner: http::HeaderMap<HeaderValue>) -> Self {
		Self(inner)
	}

	/// Insert a new key and value into the header.
	/// 
	/// If a value to this key is already present
	/// that value is dropped.
	/// 
	/// ## Panics
	/// If the value is not a valid HeaderValue.
	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)
	}

	/// Insert a new key and value into the header. Returning
	/// None if the value is not valid.
	/// 
	/// If a value to this key is already present
	/// that value is dropped.
	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()?))
	}

	/// Insert a new key and value into the header. Percent encoding
	/// the value if necessary.
	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)
	}

	/// Insert a new key and a serializeable value. The value will be serialized
	/// as json and percent encoded.
	/// 
	/// Returns `None` if the value could not be serialized or inserted.
	#[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))
	}

	/// Returns the value if it exists.
	pub fn get<K>(&self, key: K) -> Option<&HeaderValue>
	where K: AsHeaderName {
		self.0.get(key)
	}

	/// Returns the value mutably if it exists.
	pub fn get_mut<K>(&mut self, key: K) -> Option<&mut HeaderValue>
	where K: AsHeaderName {
		self.0.get_mut(key)
	}

	/// Returns the value as a string if it exists and is valid.
	pub fn get_str<K>(&self, key: K) -> Option<&str>
	where K: AsHeaderName {
		self.get(key).and_then(|v| v.to_str().ok())
	}

	/// Returns the value percent decoded as a string if it exists and is valid.
	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()
			})
	}

	/// Deserializes a given value. Returning `None` if the value
	/// does not exist or is not valid json.
	#[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()
	}

	/// Returns the inner `HeaderMap`.
	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();
	// does not allocate again
	let b: bytes::Bytes = s.into();
	// now lets make a header value
	// TODO probably can be changed to
	// from maybe shared unchecked
	// but need to check first control covers all cases
	HeaderValue::from_maybe_shared(b).unwrap()
}

/// Converts a value into a `HeaderValue` and encodes it if necessary.
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::{Serialize, Deserialize};

	#[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);

	}

}