http_types/headers/
header_name.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use std::borrow::Cow;
use std::fmt::{self, Debug, Display};
use std::str::FromStr;

use crate::Error;

/// A header name.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct HeaderName(Cow<'static, str>);

impl HeaderName {
    /// Create a new `HeaderName` from a Vec of ASCII bytes.
    ///
    /// # Error
    ///
    /// This function will error if the bytes is not valid ASCII.
    pub fn from_bytes(mut bytes: Vec<u8>) -> Result<Self, Error> {
        crate::ensure!(bytes.is_ascii(), "Bytes should be valid ASCII");
        bytes.make_ascii_lowercase();

        // This is permitted because ASCII is valid UTF-8, and we just checked that.
        let string = unsafe { String::from_utf8_unchecked(bytes.to_vec()) };
        Ok(HeaderName(Cow::Owned(string)))
    }

    /// Create a new `HeaderName` from an ASCII string.
    ///
    /// # Error
    ///
    /// This function will error if the string is not valid ASCII.
    pub fn from_string(s: String) -> Result<Self, Error> {
        Self::from_bytes(s.into_bytes())
    }

    /// Returns the header name as a `&str`.
    pub fn as_str(&self) -> &'_ str {
        &self.0
    }

    /// Converts a vector of bytes to a `HeaderName` without checking that the string contains
    /// valid ASCII.
    ///
    /// # Safety
    ///
    /// This function is unsafe because it does not check that the bytes passed to it are valid
    /// ASCII. If this constraint is violated, it may cause memory
    /// unsafety issues with future users of the HeaderName, as the rest of the library assumes
    /// that Strings are valid ASCII.
    pub unsafe fn from_bytes_unchecked(mut bytes: Vec<u8>) -> Self {
        bytes.make_ascii_lowercase();
        let string = String::from_utf8_unchecked(bytes);
        HeaderName(Cow::Owned(string))
    }

    /// Converts a string assumed to lowercase into a `HeaderName`
    pub(crate) const fn from_lowercase_str(str: &'static str) -> Self {
        HeaderName(Cow::Borrowed(str))
    }
}

impl Debug for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self.0)
    }
}

impl Display for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromStr for HeaderName {
    type Err = Error;

    /// Create a new `HeaderName`.
    ///
    /// This checks it's valid ASCII, and lowercases it.
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        crate::ensure!(s.is_ascii(), "String slice should be valid ASCII");
        Ok(HeaderName(Cow::Owned(s.to_ascii_lowercase())))
    }
}

impl From<&HeaderName> for HeaderName {
    fn from(value: &HeaderName) -> HeaderName {
        value.clone()
    }
}

impl<'a> From<&'a str> for HeaderName {
    fn from(value: &'a str) -> Self {
        Self::from_str(value).expect("String slice should be valid ASCII")
    }
}

impl PartialEq<str> for HeaderName {
    fn eq(&self, other: &str) -> bool {
        match HeaderName::from_str(other) {
            Err(_) => false,
            Ok(other) => self == &other,
        }
    }
}

impl<'a> PartialEq<&'a str> for HeaderName {
    fn eq(&self, other: &&'a str) -> bool {
        match HeaderName::from_str(other) {
            Err(_) => false,
            Ok(other) => self == &other,
        }
    }
}

impl PartialEq<String> for HeaderName {
    fn eq(&self, other: &String) -> bool {
        match HeaderName::from_str(other) {
            Err(_) => false,
            Ok(other) => self == &other,
        }
    }
}

impl<'a> PartialEq<&String> for HeaderName {
    fn eq(&self, other: &&String) -> bool {
        match HeaderName::from_str(other) {
            Err(_) => false,
            Ok(other) => self == &other,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[allow(clippy::eq_op)]
    fn test_header_name_static_non_static() {
        let static_header = HeaderName::from_lowercase_str("hello");
        let non_static_header = HeaderName::from_str("hello").unwrap();

        assert_eq!(&static_header, &non_static_header);
        assert_eq!(&static_header, &static_header);
        assert_eq!(&non_static_header, &non_static_header);

        assert_eq!(static_header, non_static_header);
        assert_eq!(static_header, static_header);
        assert_eq!(non_static_header, non_static_header);
    }

    #[test]
    fn equality() {
        let static_header = HeaderName::from_lowercase_str("hello");
        assert_eq!(static_header, "hello");
        assert_eq!(&static_header, "hello");
        assert_eq!(static_header, String::from("hello"));
        assert_eq!(static_header, &String::from("hello"));

        // Must validate regardless of casing.
        assert_eq!(static_header, &String::from("Hello"));
    }

    #[test]
    fn pass_name_by_ref() {
        let mut res = crate::Response::new(200);
        res.insert_header(&crate::headers::HOST, "127.0.0.1");
    }

    #[test]
    fn test_debug() {
        let header_name = HeaderName::from_str("hello").unwrap();
        assert_eq!(format!("{:?}", header_name), "\"hello\"");
    }
}