fire_http_representation/header/
values.rs1use 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#[derive(Debug, Clone)]
17pub struct HeaderValues(http::HeaderMap<HeaderValue>);
18
19impl HeaderValues {
20 pub fn new() -> Self {
22 Self(http::HeaderMap::new())
23 }
24
25 pub fn from_inner(inner: http::HeaderMap<HeaderValue>) -> Self {
27 Self(inner)
28 }
29
30 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 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 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 #[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 pub fn get<K>(&self, key: K) -> Option<&HeaderValue>
96 where
97 K: AsHeaderName,
98 {
99 self.0.get(key)
100 }
101
102 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 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 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 #[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 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 let b: bytes::Bytes = s.into();
164 HeaderValue::from_maybe_shared(b).unwrap()
169}
170
171pub 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}