1use std::{fmt::Display, str::FromStr};
14
15use algorithm::buf::{Bt, BtMut};
16use crate::{byte_map, Helper, HttpError, Serialize, WebError, WebResult};
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash)]
19pub enum Scheme {
20 None,
21 Http,
22 Https,
23 Ws,
24 Wss,
25 Ftp,
26 Extension(String),
27}
28
29impl Scheme {
30 const MAX_SCHEME_LEN: usize = 64;
31 const SCHEME_MAP: [bool; 256] = byte_map![
35 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
39 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
41 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
43 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
45 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
47 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
49 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55 0, 0, 0, 0, 0, 0, 0,
56 ];
57
58 #[inline]
59 pub(crate) fn is_scheme_token(b: u8) -> bool {
60 Self::SCHEME_MAP[b as usize]
61 }
62
63 pub fn parse_scheme<T: Bt>(buffer: &mut T) -> WebResult<Scheme> {
64 let scheme = Helper::parse_scheme(buffer)?;
65 if scheme.as_bytes().len() > Self::MAX_SCHEME_LEN {
66 return Err(WebError::Http(HttpError::SchemeTooLong));
67 }
68 Scheme::try_from(scheme)
69 }
70
71 pub fn as_str(&self) -> &str {
72 match self {
73 Scheme::Http => "http",
74 Scheme::Https => "https",
75 Scheme::Ws => "ws",
76 Scheme::Wss => "wss",
77 Scheme::Ftp => "ftp",
78 Scheme::Extension(s) => &s.as_str(),
79 Scheme::None => "",
80 }
81 }
82
83 pub fn is_none(&self) -> bool {
84 match self {
85 Scheme::None => true,
86 _ => false,
87 }
88 }
89
90 pub fn is_http(&self) -> bool {
91 match self {
92 Scheme::Http => true,
93 _ => false,
94 }
95 }
96
97 pub fn is_https(&self) -> bool {
98 match self {
99 Scheme::Https => true,
100 _ => false,
101 }
102 }
103
104
105 pub fn is_ws(&self) -> bool {
106 match self {
107 Scheme::Ws => true,
108 _ => false,
109 }
110 }
111
112 pub fn is_wss(&self) -> bool {
113 match self {
114 Scheme::Wss => true,
115 _ => false,
116 }
117 }
118}
119
120impl Display for Scheme {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 f.write_str(&self.as_str())
123 }
124}
125
126impl Serialize for Scheme {
127 fn serialize<B: Bt + BtMut>(&mut self, buffer: &mut B) -> WebResult<usize> {
128 match self {
129 Scheme::None => Err(WebError::Serialize("scheme")),
130 _ => Ok(buffer.put_slice(self.as_str().as_bytes())),
131 }
132 }
133}
134
135impl TryFrom<&str> for Scheme {
136 type Error = WebError;
137
138 fn try_from(value: &str) -> Result<Self, Self::Error> {
139 if value.len() > 64 {
140 return Err(WebError::from(crate::UrlError::UrlInvalid));
141 }
142 match value {
143 "http" => Ok(Scheme::Http),
144 "https" => Ok(Scheme::Https),
145 "ws" => Ok(Scheme::Ws),
146 "wss" => Ok(Scheme::Wss),
147 "ftp" => Ok(Scheme::Ftp),
148 _ => Ok(Scheme::Extension(value.to_string())),
149 }
150 }
151}
152
153impl FromStr for Scheme {
154 type Err = WebError;
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 Scheme::try_from(&*s.to_uppercase())
157 }
158}