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