trillium_http/headers/
header_value.rs1use HeaderValueInner::{Bytes, Utf8};
2use smartcow::SmartCow;
3use smartstring::SmartString;
4use std::{
5 borrow::Cow,
6 fmt::{Debug, Display, Formatter, Write},
7};
8
9#[derive(Clone)]
12pub struct HeaderValue {
13 pub(crate) inner: HeaderValueInner,
14 pub(crate) never_indexed: bool,
19}
20
21impl HeaderValue {
22 pub(crate) const fn from_inner(inner: HeaderValueInner) -> Self {
23 Self {
24 inner,
25 never_indexed: false,
26 }
27 }
28
29 pub(crate) fn is_never_indexed(&self) -> bool {
30 self.never_indexed
31 }
32
33 pub(crate) fn set_never_indexed(&mut self, never_indexed: bool) {
34 self.never_indexed = never_indexed;
35 }
36}
37
38impl PartialEq for HeaderValue {
39 fn eq(&self, other: &Self) -> bool {
40 self.inner == other.inner
41 }
42}
43
44impl Eq for HeaderValue {}
45
46impl std::hash::Hash for HeaderValue {
47 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48 self.inner.hash(state);
49 }
50}
51
52impl PartialOrd for HeaderValue {
53 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
54 Some(self.cmp(other))
55 }
56}
57
58impl Ord for HeaderValue {
59 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
60 self.inner.cmp(&other.inner)
61 }
62}
63
64impl From<Cow<'static, [u8]>> for HeaderValue {
65 fn from(value: Cow<'static, [u8]>) -> Self {
66 match value {
67 Cow::Borrowed(bytes) => match std::str::from_utf8(bytes) {
68 Ok(s) => Self::from_inner(Utf8(SmartCow::Borrowed(s))),
69 Err(_) => Self::from_inner(Bytes(bytes.into())),
70 },
71
72 Cow::Owned(bytes) => match String::from_utf8(bytes) {
73 Ok(s) => Self::from_inner(Utf8(SmartCow::Owned(s.into()))),
74 Err(e) => Self::from_inner(Bytes(e.into_bytes().into())),
75 },
76 }
77 }
78}
79
80#[derive(Eq, PartialEq, Clone, Hash)]
81pub(crate) enum HeaderValueInner {
82 Utf8(SmartCow<'static>),
83 Bytes(Box<[u8]>),
84}
85
86impl PartialOrd for HeaderValueInner {
87 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
88 Some(self.cmp(other))
89 }
90}
91impl Ord for HeaderValueInner {
92 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
93 let this: &[u8] = self.as_ref();
94 let that: &[u8] = other.as_ref();
95 this.cmp(that)
96 }
97}
98
99#[cfg(feature = "serde")]
100impl serde::Serialize for HeaderValue {
101 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102 where
103 S: serde::Serializer,
104 {
105 match &self.inner {
106 Utf8(s) => serializer.serialize_str(s),
107 Bytes(bytes) => serializer.serialize_bytes(bytes),
108 }
109 }
110}
111
112impl Debug for HeaderValue {
113 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114 match &self.inner {
115 Utf8(s) => Debug::fmt(s, f),
116 Bytes(b) => Debug::fmt(&String::from_utf8_lossy(b), f),
117 }
118 }
119}
120
121impl HeaderValue {
122 pub const fn const_new(value: &'static str) -> Self {
124 Self::from_inner(Utf8(SmartCow::Borrowed(value)))
125 }
126
127 pub fn is_valid(&self) -> bool {
129 memchr::memchr3(b'\r', b'\n', 0, self.as_ref()).is_none()
130 }
131
132 pub fn as_str(&self) -> Option<&str> {
135 match &self.inner {
136 Utf8(utf8) => Some(utf8),
137 Bytes(_) => None,
138 }
139 }
140}
141
142#[cfg(feature = "parse")]
143impl HeaderValue {
144 pub(crate) fn parse(bytes: &[u8]) -> Self {
145 match std::str::from_utf8(bytes) {
146 Ok(s) => Self::from_inner(Utf8(SmartCow::Owned(s.into()))),
147 Err(_) => Self::from_inner(Bytes(bytes.into())),
148 }
149 }
150}
151
152impl Display for HeaderValue {
153 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
154 match &self.inner {
155 Utf8(s) => f.write_str(s),
156 Bytes(b) => f.write_str(&String::from_utf8_lossy(b)),
157 }
158 }
159}
160
161impl From<Vec<u8>> for HeaderValue {
162 fn from(v: Vec<u8>) -> Self {
163 match String::from_utf8(v) {
164 Ok(s) => Self::from_inner(Utf8(SmartCow::Owned(s.into()))),
165 Err(e) => Self::from_inner(Bytes(e.into_bytes().into())),
166 }
167 }
168}
169
170impl From<Cow<'static, str>> for HeaderValue {
171 fn from(c: Cow<'static, str>) -> Self {
172 Self::from_inner(Utf8(SmartCow::from(c)))
173 }
174}
175
176impl From<&'static [u8]> for HeaderValue {
177 fn from(b: &'static [u8]) -> Self {
178 match std::str::from_utf8(b) {
179 Ok(s) => Self::from_inner(Utf8(SmartCow::Borrowed(s))),
180 Err(_) => Self::from_inner(Bytes(b.into())),
181 }
182 }
183}
184
185impl From<String> for HeaderValue {
186 fn from(s: String) -> Self {
187 Self::from_inner(Utf8(SmartCow::Owned(s.into())))
188 }
189}
190
191impl From<&'static str> for HeaderValue {
192 fn from(s: &'static str) -> Self {
193 Self::from_inner(Utf8(SmartCow::Borrowed(s)))
194 }
195}
196
197macro_rules! delegate_from_to_format {
198 ($($t:ty),*) => {
199 $(
200 impl From<$t> for HeaderValue {
201 fn from(value: $t) -> Self {
202 format_args!("{value}").into()
203 }
204 }
205 )*
206 };
207}
208
209delegate_from_to_format!(usize, u64, u16, u32, i32, i64);
210
211impl From<std::fmt::Arguments<'_>> for HeaderValue {
212 fn from(value: std::fmt::Arguments<'_>) -> Self {
213 let mut s = SmartString::new();
214 s.write_fmt(value).unwrap();
215 Self::from_inner(Utf8(SmartCow::Owned(s)))
216 }
217}
218
219impl AsRef<[u8]> for HeaderValueInner {
220 fn as_ref(&self) -> &[u8] {
221 match self {
222 Utf8(utf8) => utf8.as_bytes(),
223 Bytes(b) => b,
224 }
225 }
226}
227
228impl AsRef<[u8]> for HeaderValue {
229 fn as_ref(&self) -> &[u8] {
230 self.inner.as_ref()
231 }
232}
233
234impl PartialEq<&str> for HeaderValue {
235 fn eq(&self, other: &&str) -> bool {
236 self.as_str() == Some(*other)
237 }
238}
239
240impl PartialEq<&[u8]> for HeaderValue {
241 fn eq(&self, other: &&[u8]) -> bool {
242 self.as_ref() == *other
243 }
244}
245
246impl PartialEq<[u8]> for HeaderValue {
247 fn eq(&self, other: &[u8]) -> bool {
248 self.as_ref() == other
249 }
250}
251
252impl PartialEq<str> for HeaderValue {
253 fn eq(&self, other: &str) -> bool {
254 self.as_str() == Some(other)
255 }
256}
257
258impl PartialEq<String> for HeaderValue {
259 fn eq(&self, other: &String) -> bool {
260 self.as_str() == Some(other)
261 }
262}
263
264impl PartialEq<&String> for HeaderValue {
265 fn eq(&self, other: &&String) -> bool {
266 self.as_str() == Some(&**other)
267 }
268}
269
270impl PartialEq<[u8]> for &HeaderValue {
271 fn eq(&self, other: &[u8]) -> bool {
272 self.as_ref() == other
273 }
274}
275
276impl PartialEq<str> for &HeaderValue {
277 fn eq(&self, other: &str) -> bool {
278 self.as_str() == Some(other)
279 }
280}
281
282impl PartialEq<String> for &HeaderValue {
283 fn eq(&self, other: &String) -> bool {
284 self.as_str() == Some(other)
285 }
286}