uniform_resource_identifier/uri/scheme/
mod.rs1use lazy_static::lazy_static;
2use std::error::Error;
3use std::fmt;
4
5use crate::Port;
6
7#[derive(Debug, Clone)]
8pub enum SchemeError {
9 InvalidScheme,
10}
11
12impl fmt::Display for SchemeError {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 match self {
15 SchemeError::InvalidScheme => write!(f, "Invalid scheme."),
16 }
17 }
18}
19
20impl Error for SchemeError {}
21
22#[derive(Debug, Clone, PartialEq)]
23pub enum Scheme {
24 Http,
25 Https,
26 Ws,
27 Wss,
28 File,
29 Scheme(String),
30}
31
32impl From<Scheme> for Port {
33 fn from(scheme: Scheme) -> Port {
34 match scheme {
35 Scheme::Http => Port::Http,
36 Scheme::Https => Port::Https,
37 _ => panic!(""),
38 }
39 }
40}
41
42impl ToString for Scheme {
43 fn to_string(&self) -> String {
44 match self {
45 Scheme::Http => String::from("http"),
46 Scheme::Https => String::from("https"),
47 Scheme::Ws => String::from("ws"),
48 Scheme::Wss => String::from("wss"),
49 Scheme::File => String::from("file"),
50 Scheme::Scheme(scheme) => scheme.clone(),
51 }
52 }
53}
54
55#[derive(Debug, PartialEq)]
56pub struct SchemeChar {
57 code: u8,
58 next_char: Vec<SchemeChar>,
59 scheme: Option<Scheme>,
60}
61
62fn add_scheme(mut chars: &mut Vec<SchemeChar>, value: &[u8], enum_value: Scheme) {
63 let end_idx = value.len() - 1;
64
65 for (idx, code) in value.to_ascii_lowercase().iter().enumerate() {
66 let mut search_result = chars.binary_search_by(|x| (x.code).cmp(code));
67 if search_result.is_err() {
68 let scheme = if idx == end_idx {
69 Some(enum_value.clone())
70 } else {
71 None
72 };
73 chars.push(SchemeChar {
74 code: *code,
75 next_char: Vec::new(),
76 scheme,
77 });
78 chars.sort_unstable_by(|a, b| a.code.cmp(&b.code));
79 search_result = chars.binary_search_by(|x| (x.code).cmp(code));
80 }
81 let index = search_result.unwrap();
82 chars = &mut chars[index].next_char;
83 }
84}
85
86lazy_static! {
87 pub static ref SCHEME_CHARS: Vec<SchemeChar> = {
88 let mut chars = Vec::<SchemeChar>::new();
89 add_scheme(&mut chars, b"http", Scheme::Http);
90 add_scheme(&mut chars, b"https", Scheme::Https);
91 add_scheme(&mut chars, b"ws", Scheme::Ws);
92 add_scheme(&mut chars, b"wss", Scheme::Wss);
93 add_scheme(&mut chars, b"file", Scheme::File);
94 chars
95 };
96}
97
98pub fn get_scheme(value: &[u8]) -> Result<Scheme, SchemeError> {
99 let mut chars = &*SCHEME_CHARS;
100 let end_idx = value.len() - 1;
101 for (idx, code) in value.to_ascii_lowercase().iter().enumerate() {
102 let search_result = chars.binary_search_by(|header_char| (header_char.code).cmp(code));
103 let index = search_result.map_err(|_| SchemeError::InvalidScheme)?;
104 if idx == end_idx {
105 return chars[index]
106 .scheme
107 .clone()
108 .ok_or(SchemeError::InvalidScheme);
109 }
110 chars = &chars[index].next_char;
111 }
112 Err(SchemeError::InvalidScheme)
113}
114
115macro_rules! char_colon {
116 () => {
117 0x3a
118 };
119}
120
121pub fn parse_scheme(
122 input: &[u8],
123 start: &mut usize,
124 end: &usize,
125) -> Result<Scheme, Box<dyn Error>> {
126 let mut index = *start;
127
128 while index < *end
129 && ((input[index] >= 0x41 && input[index] <= 0x5a)
130 || (input[index] >= 0x61 && input[index] <= 0x7a))
131 {
132 index += 1
133 }
134
135 if input[index] == char_colon!() {
136 index -= 1;
137 }
138
139 let s = get_scheme(&input[*start..=index])?;
140
141 *start = index + 2;
142
143 Ok(s)
144}
145
146#[cfg(test)]
147mod test_scheme {
148 use crate::uri::scheme::parse_scheme;
149 use crate::{Port, Scheme};
150
151 #[test]
152 fn scheme_to_port() {
153 let scheme = Scheme::Http;
154 let port: Port = scheme.into();
155 assert_eq!(port, Port::Http);
156 }
157
158 #[test]
159 fn scheme_to_string() {
160 let scheme = Scheme::Http;
161 assert_eq!(scheme.to_string(), "http");
162 }
163
164 #[test]
165 fn test_parse_scheme_1() {
166 use parse_scheme;
167
168 let s = b"https";
169 let l = s.len() - 1;
170 let mut c = 0;
171 let scheme = parse_scheme(s, &mut c, &l).unwrap();
172
173 assert_eq!(scheme, Scheme::Https);
174 assert_eq!(c, 6);
175 }
176
177 #[test]
178 fn test_parse_scheme_2() {
179 use parse_scheme;
180
181 let s = b"https://";
182 let l = s.len() - 1;
183 let mut c = 0;
184 let scheme = parse_scheme(s, &mut c, &l).unwrap();
185
186 assert_eq!(scheme, Scheme::Https);
187 assert_eq!(c, 6);
188 }
189}