Skip to main content

trillium_http/headers/
unknown_header_name.rs

1use super::{HeaderName, HeaderNameInner::UnknownHeader};
2use hashbrown::Equivalent;
3use smartcow::SmartCow;
4use std::{
5    cmp::Ordering,
6    fmt::{self, Debug, Display, Formatter},
7    hash::{Hash, Hasher},
8    ops::Deref,
9};
10
11#[derive(Clone)]
12pub(super) struct UnknownHeaderName<'a>(SmartCow<'a>);
13
14impl PartialOrd for UnknownHeaderName<'_> {
15    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
16        Some(self.cmp(other))
17    }
18}
19
20impl Ord for UnknownHeaderName<'_> {
21    fn cmp(&self, other: &Self) -> Ordering {
22        self.0.cmp(&*other.0)
23    }
24}
25
26impl PartialEq for UnknownHeaderName<'_> {
27    fn eq(&self, other: &Self) -> bool {
28        self.0.eq_ignore_ascii_case(&other.0)
29    }
30}
31
32impl Eq for UnknownHeaderName<'_> {}
33
34impl Hash for UnknownHeaderName<'_> {
35    fn hash<H: Hasher>(&self, state: &mut H) {
36        for c in self.0.as_bytes() {
37            c.to_ascii_lowercase().hash(state);
38        }
39    }
40}
41
42impl Debug for UnknownHeaderName<'_> {
43    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44        Debug::fmt(&self.0, f)
45    }
46}
47
48impl Display for UnknownHeaderName<'_> {
49    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
50        Display::fmt(&self.0, f)
51    }
52}
53
54impl<'a> From<UnknownHeaderName<'a>> for HeaderName<'a> {
55    fn from(value: UnknownHeaderName<'a>) -> Self {
56        HeaderName(UnknownHeader(value))
57    }
58}
59
60fn is_tchar(c: char) -> bool {
61    matches!(
62        c,
63        'a'..='z'
64        | 'A'..='Z'
65        | '0'..='9'
66        | '!'
67        | '#'
68        | '$'
69        | '%'
70        | '&'
71        | '\''
72        | '*'
73        | '+'
74        | '-'
75        | '.'
76        | '^'
77        | '_'
78        | '`'
79        | '|'
80        | '~'
81    )
82}
83
84impl UnknownHeaderName<'_> {
85    pub(crate) fn is_valid(&self) -> bool {
86        // token per https://www.rfc-editor.org/rfc/rfc9110#section-5.1
87        // tchar per https://www.rfc-editor.org/rfc/rfc9110#section-5.6.2
88        !self.is_empty() && self.0.chars().all(is_tchar)
89    }
90
91    pub(crate) fn into_owned(self) -> UnknownHeaderName<'static> {
92        UnknownHeaderName(self.0.into_owned())
93    }
94}
95
96impl<'a> UnknownHeaderName<'a> {
97    pub(crate) fn reborrow<'b: 'a>(&'b self) -> UnknownHeaderName<'b> {
98        Self(self.0.borrow())
99    }
100}
101
102impl From<String> for UnknownHeaderName<'static> {
103    fn from(value: String) -> Self {
104        Self(value.into())
105    }
106}
107
108impl<'a> From<&'a str> for UnknownHeaderName<'a> {
109    fn from(value: &'a str) -> Self {
110        Self(value.into())
111    }
112}
113
114impl<'a> From<SmartCow<'a>> for UnknownHeaderName<'a> {
115    fn from(value: SmartCow<'a>) -> Self {
116        Self(value)
117    }
118}
119
120impl<'a> From<UnknownHeaderName<'a>> for SmartCow<'a> {
121    fn from(value: UnknownHeaderName<'a>) -> Self {
122        value.0
123    }
124}
125
126impl Deref for UnknownHeaderName<'_> {
127    type Target = str;
128
129    fn deref(&self) -> &Self::Target {
130        &self.0
131    }
132}
133
134impl Equivalent<UnknownHeaderName<'_>> for &UnknownHeaderName<'_> {
135    fn equivalent(&self, key: &UnknownHeaderName<'_>) -> bool {
136        key.eq_ignore_ascii_case(self)
137    }
138}