Skip to main content

trillium_http/headers/
header_value.rs

1use 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/// A `HeaderValue` represents the right hand side of a single `name:
11/// value` pair.
12#[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    /// Build a new header value from a &'static str at compile time
74    pub const fn const_new(value: &'static str) -> Self {
75        Self(Utf8(SmartCow::Borrowed(value)))
76    }
77
78    /// determine if this header contains no unsafe characters (\r, \n, \0)
79    ///
80    /// since 0.3.12
81    pub fn is_valid(&self) -> bool {
82        memchr::memchr3(b'\r', b'\n', 0, self.as_ref()).is_none()
83    }
84
85    /// Returns this header value as a &str if it is utf8, None
86    /// otherwise. If you need to convert non-utf8 bytes to a string
87    /// somehow, match directly on the `HeaderValue` as an enum and
88    /// handle that case. If you need a byte slice regardless of
89    /// whether it's utf8, use the `AsRef<[u8]>` impl
90    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}