http_types/headers/
header_name.rs
1use std::borrow::Cow;
2use std::fmt::{self, Debug, Display};
3use std::str::FromStr;
4
5use crate::Error;
6
7#[derive(Clone, PartialEq, Eq, Hash)]
9pub struct HeaderName(Cow<'static, str>);
10
11impl HeaderName {
12 pub fn from_bytes(mut bytes: Vec<u8>) -> Result<Self, Error> {
18 crate::ensure!(bytes.is_ascii(), "Bytes should be valid ASCII");
19 bytes.make_ascii_lowercase();
20
21 let string = unsafe { String::from_utf8_unchecked(bytes.to_vec()) };
23 Ok(HeaderName(Cow::Owned(string)))
24 }
25
26 pub fn from_string(s: String) -> Result<Self, Error> {
32 Self::from_bytes(s.into_bytes())
33 }
34
35 pub fn as_str(&self) -> &'_ str {
37 &self.0
38 }
39
40 pub unsafe fn from_bytes_unchecked(mut bytes: Vec<u8>) -> Self {
50 bytes.make_ascii_lowercase();
51 let string = String::from_utf8_unchecked(bytes);
52 HeaderName(Cow::Owned(string))
53 }
54
55 pub(crate) const fn from_lowercase_str(str: &'static str) -> Self {
57 HeaderName(Cow::Borrowed(str))
58 }
59}
60
61impl Debug for HeaderName {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(f, "{:?}", self.0)
64 }
65}
66
67impl Display for HeaderName {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "{}", self.0)
70 }
71}
72
73impl FromStr for HeaderName {
74 type Err = Error;
75
76 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 crate::ensure!(s.is_ascii(), "String slice should be valid ASCII");
81 Ok(HeaderName(Cow::Owned(s.to_ascii_lowercase())))
82 }
83}
84
85impl From<&HeaderName> for HeaderName {
86 fn from(value: &HeaderName) -> HeaderName {
87 value.clone()
88 }
89}
90
91impl<'a> From<&'a str> for HeaderName {
92 fn from(value: &'a str) -> Self {
93 Self::from_str(value).expect("String slice should be valid ASCII")
94 }
95}
96
97impl PartialEq<str> for HeaderName {
98 fn eq(&self, other: &str) -> bool {
99 match HeaderName::from_str(other) {
100 Err(_) => false,
101 Ok(other) => self == &other,
102 }
103 }
104}
105
106impl<'a> PartialEq<&'a str> for HeaderName {
107 fn eq(&self, other: &&'a str) -> bool {
108 match HeaderName::from_str(other) {
109 Err(_) => false,
110 Ok(other) => self == &other,
111 }
112 }
113}
114
115impl PartialEq<String> for HeaderName {
116 fn eq(&self, other: &String) -> bool {
117 match HeaderName::from_str(other) {
118 Err(_) => false,
119 Ok(other) => self == &other,
120 }
121 }
122}
123
124impl<'a> PartialEq<&String> for HeaderName {
125 fn eq(&self, other: &&String) -> bool {
126 match HeaderName::from_str(other) {
127 Err(_) => false,
128 Ok(other) => self == &other,
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 #[allow(clippy::eq_op)]
139 fn test_header_name_static_non_static() {
140 let static_header = HeaderName::from_lowercase_str("hello");
141 let non_static_header = HeaderName::from_str("hello").unwrap();
142
143 assert_eq!(&static_header, &non_static_header);
144 assert_eq!(&static_header, &static_header);
145 assert_eq!(&non_static_header, &non_static_header);
146
147 assert_eq!(static_header, non_static_header);
148 assert_eq!(static_header, static_header);
149 assert_eq!(non_static_header, non_static_header);
150 }
151
152 #[test]
153 fn equality() {
154 let static_header = HeaderName::from_lowercase_str("hello");
155 assert_eq!(static_header, "hello");
156 assert_eq!(&static_header, "hello");
157 assert_eq!(static_header, String::from("hello"));
158 assert_eq!(static_header, &String::from("hello"));
159
160 assert_eq!(static_header, &String::from("Hello"));
162 }
163
164 #[test]
165 fn pass_name_by_ref() {
166 let mut res = crate::Response::new(200);
167 res.insert_header(&crate::headers::HOST, "127.0.0.1");
168 }
169
170 #[test]
171 fn test_debug() {
172 let header_name = HeaderName::from_str("hello").unwrap();
173 assert_eq!(format!("{:?}", header_name), "\"hello\"");
174 }
175}