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)]
13pub struct HeaderValue(pub(crate) HeaderValueInner);
14
15#[derive(Eq, PartialEq, Clone)]
16pub(crate) enum HeaderValueInner {
17    Utf8(SmartCow<'static>),
18    Bytes(SmallVec<[u8; 32]>),
19}
20
21impl PartialOrd for HeaderValueInner {
22    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
23        Some(self.cmp(other))
24    }
25}
26impl Ord for HeaderValueInner {
27    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
28        let this: &[u8] = self.as_ref();
29        let that: &[u8] = other.as_ref();
30        this.cmp(that)
31    }
32}
33
34#[cfg(feature = "serde")]
35impl serde::Serialize for HeaderValue {
36    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37    where
38        S: serde::Serializer,
39    {
40        match &self.0 {
41            Utf8(s) => serializer.serialize_str(s),
42            Bytes(bytes) => serializer.serialize_bytes(bytes),
43        }
44    }
45}
46
47impl Debug for HeaderValue {
48    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49        match &self.0 {
50            Utf8(s) => Debug::fmt(s, f),
51            Bytes(b) => Debug::fmt(&String::from_utf8_lossy(b), f),
52        }
53    }
54}
55
56impl HeaderValue {
57    /// Build a new header value from a &'static str at compile time
58    pub const fn const_new(value: &'static str) -> Self {
59        Self(Utf8(SmartCow::Borrowed(value)))
60    }
61
62    /// determine if this header contains no unsafe characters (\r, \n, \0)
63    ///
64    /// since 0.3.12
65    pub fn is_valid(&self) -> bool {
66        memchr::memchr3(b'\r', b'\n', 0, self.as_ref()).is_none()
67    }
68
69    /// Returns this header value as a &str if it is utf8, None
70    /// otherwise. If you need to convert non-utf8 bytes to a string
71    /// somehow, match directly on the `HeaderValue` as an enum and
72    /// handle that case. If you need a byte slice regardless of
73    /// whether it's utf8, use the `AsRef<[u8]>` impl
74    pub fn as_str(&self) -> Option<&str> {
75        match &self.0 {
76            Utf8(utf8) => Some(utf8),
77            Bytes(_) => None,
78        }
79    }
80}
81
82impl HeaderValue {
83    pub(crate) fn parse(bytes: &[u8]) -> Self {
84        match std::str::from_utf8(bytes) {
85            Ok(s) => Self(Utf8(SmartCow::Owned(s.into()))),
86            Err(_) => Self(Bytes(bytes.into())),
87        }
88    }
89}
90
91impl Display for HeaderValue {
92    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93        match &self.0 {
94            Utf8(s) => f.write_str(s),
95            Bytes(b) => f.write_str(&String::from_utf8_lossy(b)),
96        }
97    }
98}
99
100impl From<Vec<u8>> for HeaderValue {
101    fn from(v: Vec<u8>) -> Self {
102        match String::from_utf8(v) {
103            Ok(s) => Self(Utf8(SmartCow::Owned(s.into()))),
104            Err(e) => Self(Bytes(e.into_bytes().into())),
105        }
106    }
107}
108
109impl From<Cow<'static, str>> for HeaderValue {
110    fn from(c: Cow<'static, str>) -> Self {
111        Self(Utf8(SmartCow::from(c)))
112    }
113}
114
115impl From<&'static [u8]> for HeaderValue {
116    fn from(b: &'static [u8]) -> Self {
117        match std::str::from_utf8(b) {
118            Ok(s) => Self(Utf8(SmartCow::Borrowed(s))),
119            Err(_) => Self(Bytes(b.into())),
120        }
121    }
122}
123
124impl From<String> for HeaderValue {
125    fn from(s: String) -> Self {
126        Self(Utf8(SmartCow::Owned(s.into())))
127    }
128}
129
130impl From<&'static str> for HeaderValue {
131    fn from(s: &'static str) -> Self {
132        Self(Utf8(SmartCow::Borrowed(s)))
133    }
134}
135
136macro_rules! delegate_from_to_format {
137    ($($t:ty),*) => {
138        $(
139        impl From<$t> for HeaderValue {
140            fn from(value: $t) -> Self {
141                format_args!("{value}").into()
142            }
143        }
144        )*
145    };
146}
147
148delegate_from_to_format!(usize, u64, u16, u32, i32, i64);
149
150impl From<std::fmt::Arguments<'_>> for HeaderValue {
151    fn from(value: std::fmt::Arguments<'_>) -> Self {
152        let mut s = SmartString::new();
153        s.write_fmt(value).unwrap();
154        Self(Utf8(SmartCow::Owned(s)))
155    }
156}
157
158impl AsRef<[u8]> for HeaderValueInner {
159    fn as_ref(&self) -> &[u8] {
160        match self {
161            Utf8(utf8) => utf8.as_bytes(),
162            Bytes(b) => b,
163        }
164    }
165}
166
167impl AsRef<[u8]> for HeaderValue {
168    fn as_ref(&self) -> &[u8] {
169        self.0.as_ref()
170    }
171}
172
173impl PartialEq<&str> for HeaderValue {
174    fn eq(&self, other: &&str) -> bool {
175        self.as_str() == Some(*other)
176    }
177}
178
179impl PartialEq<&[u8]> for HeaderValue {
180    fn eq(&self, other: &&[u8]) -> bool {
181        self.as_ref() == *other
182    }
183}
184
185impl PartialEq<[u8]> for HeaderValue {
186    fn eq(&self, other: &[u8]) -> bool {
187        self.as_ref() == other
188    }
189}
190
191impl PartialEq<str> for HeaderValue {
192    fn eq(&self, other: &str) -> bool {
193        self.as_str() == Some(other)
194    }
195}
196
197impl PartialEq<String> for HeaderValue {
198    fn eq(&self, other: &String) -> bool {
199        self.as_str() == Some(other)
200    }
201}
202
203impl PartialEq<&String> for HeaderValue {
204    fn eq(&self, other: &&String) -> bool {
205        self.as_str() == Some(&**other)
206    }
207}
208
209impl PartialEq<[u8]> for &HeaderValue {
210    fn eq(&self, other: &[u8]) -> bool {
211        self.as_ref() == other
212    }
213}
214
215impl PartialEq<str> for &HeaderValue {
216    fn eq(&self, other: &str) -> bool {
217        self.as_str() == Some(other)
218    }
219}
220
221impl PartialEq<String> for &HeaderValue {
222    fn eq(&self, other: &String) -> bool {
223        self.as_str() == Some(other)
224    }
225}