rama_net/forwarded/
proto.rs1use core::str::FromStr;
2
3use crate::std::string::String;
4
5use crate::Protocol;
6
7use rama_utils::macros::{error::static_str_error, str::eq_ignore_ascii_case};
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct ForwardedProtocol(ProtocolKind);
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22enum ProtocolKind {
23 Http,
25 Https,
27}
28
29const HTTP_STR: &str = "http";
30const HTTPS_STR: &str = "https";
31
32impl ForwardedProtocol {
33 pub const HTTP: Self = Self(ProtocolKind::Http);
35
36 pub const HTTPS: Self = Self(ProtocolKind::Https);
38
39 #[must_use]
41 pub fn is_http(&self) -> bool {
42 match &self.0 {
43 ProtocolKind::Http | ProtocolKind::Https => true,
44 }
45 }
46
47 #[must_use]
49 pub fn is_secure(&self) -> bool {
50 match self.0 {
51 ProtocolKind::Https => true,
52 ProtocolKind::Http => false,
53 }
54 }
55
56 #[must_use]
58 pub fn as_scheme(&self) -> &str {
59 match &self.0 {
60 ProtocolKind::Https => HTTPS_STR,
61 ProtocolKind::Http => HTTP_STR,
62 }
63 }
64
65 #[inline]
66 #[must_use]
68 pub fn into_protocol(self) -> Protocol {
69 self.into()
70 }
71
72 #[must_use]
74 pub fn as_str(&self) -> &str {
75 match &self.0 {
76 ProtocolKind::Https => HTTPS_STR,
77 ProtocolKind::Http => HTTP_STR,
78 }
79 }
80}
81
82impl From<ForwardedProtocol> for Protocol {
83 fn from(p: ForwardedProtocol) -> Self {
84 match p.0 {
85 ProtocolKind::Https => Self::HTTPS,
86 ProtocolKind::Http => Self::HTTP,
87 }
88 }
89}
90
91static_str_error! {
92 #[doc = "unknown protocol"]
93 pub struct UnknownProtocol;
94}
95
96impl TryFrom<Protocol> for ForwardedProtocol {
97 type Error = UnknownProtocol;
98
99 fn try_from(p: Protocol) -> Result<Self, Self::Error> {
100 if p.is_http() {
101 if p.is_secure() {
102 Ok(Self(ProtocolKind::Https))
103 } else {
104 Ok(Self(ProtocolKind::Http))
105 }
106 } else {
107 Err(UnknownProtocol)
108 }
109 }
110}
111
112impl TryFrom<&Protocol> for ForwardedProtocol {
113 type Error = UnknownProtocol;
114
115 fn try_from(p: &Protocol) -> Result<Self, Self::Error> {
116 if p.is_http() {
117 if p.is_secure() {
118 Ok(Self(ProtocolKind::Https))
119 } else {
120 Ok(Self(ProtocolKind::Http))
121 }
122 } else {
123 Err(UnknownProtocol)
124 }
125 }
126}
127
128static_str_error! {
129 #[doc = "invalid forwarded protocol string"]
130 pub struct InvalidProtocolStr;
131}
132
133impl TryFrom<&str> for ForwardedProtocol {
134 type Error = InvalidProtocolStr;
135
136 fn try_from(s: &str) -> Result<Self, Self::Error> {
137 if eq_ignore_ascii_case!(s, HTTP_STR) {
138 Ok(Self(ProtocolKind::Http))
139 } else if eq_ignore_ascii_case!(s, HTTPS_STR) {
140 Ok(Self(ProtocolKind::Https))
141 } else {
142 Err(InvalidProtocolStr)
143 }
144 }
145}
146
147impl TryFrom<String> for ForwardedProtocol {
148 type Error = InvalidProtocolStr;
149
150 fn try_from(s: String) -> Result<Self, Self::Error> {
151 s.as_str().try_into()
152 }
153}
154
155impl TryFrom<&String> for ForwardedProtocol {
156 type Error = InvalidProtocolStr;
157
158 fn try_from(s: &String) -> Result<Self, Self::Error> {
159 s.as_str().try_into()
160 }
161}
162
163impl FromStr for ForwardedProtocol {
164 type Err = InvalidProtocolStr;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
167 s.try_into()
168 }
169}
170
171impl PartialEq<str> for ForwardedProtocol {
172 fn eq(&self, other: &str) -> bool {
173 match &self.0 {
174 ProtocolKind::Https => other.eq_ignore_ascii_case(HTTPS_STR),
175 ProtocolKind::Http => other.eq_ignore_ascii_case(HTTP_STR) || other.is_empty(),
176 }
177 }
178}
179
180impl PartialEq<String> for ForwardedProtocol {
181 fn eq(&self, other: &String) -> bool {
182 self == other.as_str()
183 }
184}
185
186impl PartialEq<&str> for ForwardedProtocol {
187 fn eq(&self, other: &&str) -> bool {
188 self == *other
189 }
190}
191
192impl PartialEq<ForwardedProtocol> for str {
193 fn eq(&self, other: &ForwardedProtocol) -> bool {
194 other == self
195 }
196}
197
198impl PartialEq<ForwardedProtocol> for String {
199 fn eq(&self, other: &ForwardedProtocol) -> bool {
200 other == self.as_str()
201 }
202}
203
204impl PartialEq<ForwardedProtocol> for &str {
205 #[inline(always)]
206 fn eq(&self, other: &ForwardedProtocol) -> bool {
207 other == *self
208 }
209}
210
211impl core::fmt::Display for ForwardedProtocol {
212 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213 self.as_scheme().fmt(f)
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_protocol_from_str() {
223 assert_eq!("http".parse(), Ok(ForwardedProtocol::HTTP));
224 assert_eq!("https".parse(), Ok(ForwardedProtocol::HTTPS));
225 }
226
227 #[test]
228 fn test_protocol_secure() {
229 assert!(!ForwardedProtocol::HTTP.is_secure());
230 assert!(ForwardedProtocol::HTTPS.is_secure());
231 }
232}