uniform_resource_identifier/uri/scheme/
mod.rs

1use 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}