trillium_http/headers/
header_value.rs

1use smallvec::SmallVec;
2use smartcow::SmartCow;
3use std::{
4    borrow::Cow,
5    fmt::{Debug, Display, Formatter},
6};
7use HeaderValueInner::{Bytes, Utf8};
8
9/// A `HeaderValue` represents the right hand side of a single `name:
10/// value` pair.
11#[derive(Eq, PartialEq, Clone)]
12pub struct HeaderValue(HeaderValueInner);
13
14impl HeaderValue {
15    /// determine if this header contains no unsafe characters (\r, \n, \0)
16    ///
17    /// since 0.3.12
18    pub fn is_valid(&self) -> bool {
19        memchr::memchr3(b'\r', b'\n', 0, self.as_ref()).is_none()
20    }
21}
22
23#[derive(Eq, PartialEq, Clone)]
24pub(crate) enum HeaderValueInner {
25    Utf8(SmartCow<'static>),
26    Bytes(SmallVec<[u8; 32]>),
27}
28
29#[cfg(feature = "serde")]
30impl serde::Serialize for HeaderValue {
31    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
32    where
33        S: serde::Serializer,
34    {
35        match &self.0 {
36            Utf8(s) => serializer.serialize_str(s),
37            Bytes(bytes) => serializer.serialize_bytes(bytes),
38        }
39    }
40}
41
42impl Debug for HeaderValue {
43    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
44        match &self.0 {
45            Utf8(s) => Debug::fmt(s, f),
46            Bytes(b) => Debug::fmt(&String::from_utf8_lossy(b), f),
47        }
48    }
49}
50
51impl HeaderValue {
52    /// Returns this header value as a &str if it is utf8, None
53    /// otherwise. If you need to convert non-utf8 bytes to a string
54    /// somehow, match directly on the `HeaderValue` as an enum and
55    /// handle that case. If you need a byte slice regardless of
56    /// whether it's utf8, use the `AsRef<[u8]>` impl
57    pub fn as_str(&self) -> Option<&str> {
58        match &self.0 {
59            Utf8(utf8) => Some(utf8),
60            Bytes(_) => None,
61        }
62    }
63
64    pub(crate) fn as_lower(&self) -> Option<SmartCow<'_>> {
65        self.as_str().map(|s| {
66            if s.chars().all(|c| c.is_ascii_lowercase()) {
67                SmartCow::Borrowed(s)
68            } else {
69                SmartCow::Owned(s.chars().map(|c| c.to_ascii_lowercase()).collect())
70            }
71        })
72    }
73}
74
75impl Display for HeaderValue {
76    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77        match &self.0 {
78            Utf8(s) => f.write_str(s),
79            Bytes(b) => f.write_str(&String::from_utf8_lossy(b)),
80        }
81    }
82}
83
84impl From<Vec<u8>> for HeaderValue {
85    fn from(v: Vec<u8>) -> Self {
86        match String::from_utf8(v) {
87            Ok(s) => Self(Utf8(SmartCow::Owned(s.into()))),
88            Err(e) => Self(Bytes(e.into_bytes().into())),
89        }
90    }
91}
92
93impl From<Cow<'static, str>> for HeaderValue {
94    fn from(c: Cow<'static, str>) -> Self {
95        Self(Utf8(SmartCow::from(c)))
96    }
97}
98
99impl From<&'static [u8]> for HeaderValue {
100    fn from(b: &'static [u8]) -> Self {
101        match std::str::from_utf8(b) {
102            Ok(s) => Self(Utf8(SmartCow::Borrowed(s))),
103            Err(_) => Self(Bytes(b.into())),
104        }
105    }
106}
107
108impl From<String> for HeaderValue {
109    fn from(s: String) -> Self {
110        Self(Utf8(SmartCow::Owned(s.into())))
111    }
112}
113
114impl From<&'static str> for HeaderValue {
115    fn from(s: &'static str) -> Self {
116        Self(Utf8(SmartCow::Borrowed(s)))
117    }
118}
119
120impl AsRef<[u8]> for HeaderValue {
121    fn as_ref(&self) -> &[u8] {
122        match &self.0 {
123            Utf8(utf8) => utf8.as_bytes(),
124            Bytes(b) => b,
125        }
126    }
127}
128
129impl PartialEq<&str> for HeaderValue {
130    fn eq(&self, other: &&str) -> bool {
131        self.as_str() == Some(*other)
132    }
133}
134
135impl PartialEq<&[u8]> for HeaderValue {
136    fn eq(&self, other: &&[u8]) -> bool {
137        self.as_ref() == *other
138    }
139}
140
141impl PartialEq<[u8]> for HeaderValue {
142    fn eq(&self, other: &[u8]) -> bool {
143        self.as_ref() == other
144    }
145}
146
147impl PartialEq<str> for HeaderValue {
148    fn eq(&self, other: &str) -> bool {
149        self.as_str() == Some(other)
150    }
151}
152
153impl PartialEq<String> for HeaderValue {
154    fn eq(&self, other: &String) -> bool {
155        self.as_str() == Some(other)
156    }
157}
158
159impl PartialEq<&String> for HeaderValue {
160    fn eq(&self, other: &&String) -> bool {
161        self.as_str() == Some(&**other)
162    }
163}
164
165impl PartialEq<[u8]> for &HeaderValue {
166    fn eq(&self, other: &[u8]) -> bool {
167        self.as_ref() == other
168    }
169}
170
171impl PartialEq<str> for &HeaderValue {
172    fn eq(&self, other: &str) -> bool {
173        self.as_str() == Some(other)
174    }
175}
176
177impl PartialEq<String> for &HeaderValue {
178    fn eq(&self, other: &String) -> bool {
179        self.as_str() == Some(other)
180    }
181}