rust_rcs_core/internet/
uri.rs

1// Copyright 2023 宋昊文
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::parameter::ParameterParser;
16
17// to-do: change &[u8] for &str
18pub enum URIPath<'a> {
19    AbEmpty(&'a [u8], Option<&'a [u8]>), // "//" authority *( "/" segment )
20    Absolute(&'a [u8]),                  // "/" [ segment-nz *( "/" segment ) ]
21    Rootless(&'a [u8]),                  // segment-nz *( "/" segment )
22    NoScheme(&'a [u8]),                  // segment-nz-nc *( "/" segment )
23    Empty(),
24}
25
26pub enum URI<'a> {
27    // scheme ":" hier-part [ "?" query ] [ "#" fragment ]
28    // hier-part = "//" authority path-abempty
29    //           / path-absolute
30    //           / path-rootless
31    //           / path-empty
32    Standard(&'a [u8], URIPath<'a>, Option<&'a [u8]>, Option<&'a [u8]>),
33    // relative-part [ "?" query ] [ "#" fragment ]
34    // relative-part = "//" authority path-abempty
35    //               / path-absolute
36    //               / path-noscheme
37    //               / path-empty
38    Relative(URIPath<'a>, Option<&'a [u8]>, Option<&'a [u8]>),
39}
40
41impl URI<'_> {
42    pub fn is_absolute(&self) -> bool {
43        match self {
44            URI::Standard(_, _, _, fragment) => match fragment {
45                Some(_) => false,
46                None => true,
47            },
48            _ => false,
49        }
50    }
51
52    pub fn get_query_value<'b>(&self, query_name: &'b [u8]) -> Option<&[u8]> {
53        if let Some(query_part) = match self {
54            URI::Standard(_, _, query, _) => *query,
55            URI::Relative(_, query, _) => *query,
56        } {
57            for query_parameter in ParameterParser::new(query_part, b'&', false) {
58                if query_parameter.name.eq_ignore_ascii_case(query_name) {
59                    if query_parameter.value.is_none() {
60                        return Some(&[]);
61                    } else {
62                        return query_parameter.value;
63                    }
64                }
65            }
66        }
67
68        None
69    }
70
71    pub fn string_representation_without_query_and_fragment(&self) -> Vec<u8> {
72        match self {
73            URI::Standard(scheme, hier_part, _, _) => {
74                let mut uri_string = Vec::new();
75                uri_string.extend_from_slice(scheme);
76                uri_string.extend(b":");
77                match hier_part {
78                    URIPath::AbEmpty(authority, path) => {
79                        uri_string.extend(b"//");
80                        uri_string.extend_from_slice(authority);
81                        if let Some(path) = path {
82                            uri_string.extend(b"/");
83                            uri_string.extend_from_slice(path);
84                        }
85                        uri_string
86                    }
87                    URIPath::Absolute(path) => {
88                        uri_string.extend(b"/");
89                        uri_string.extend_from_slice(path);
90                        uri_string
91                    }
92                    URIPath::Rootless(path) => {
93                        uri_string.extend_from_slice(path);
94                        uri_string
95                    }
96                    _ => uri_string,
97                }
98            }
99
100            URI::Relative(path, _, _) => {
101                let mut uri_string = Vec::new();
102                match path {
103                    URIPath::AbEmpty(authority, path) => {
104                        uri_string.extend(b"//");
105                        uri_string.extend_from_slice(authority);
106                        if let Some(path) = path {
107                            uri_string.extend(b"/");
108                            uri_string.extend_from_slice(path);
109                        }
110                        uri_string
111                    }
112                    URIPath::Absolute(path) => {
113                        uri_string.extend(b"/");
114                        uri_string.extend_from_slice(path);
115                        uri_string
116                    }
117                    URIPath::NoScheme(path) => {
118                        uri_string.extend_from_slice(path);
119                        uri_string
120                    }
121                    _ => uri_string,
122                }
123            }
124        }
125    }
126}
127
128pub trait AsURI<'a> {
129    type Target;
130    fn as_standard_uri(&'a self) -> Option<Self::Target>;
131}
132
133impl<'a> AsURI<'a> for [u8] {
134    type Target = URI<'a>;
135    fn as_standard_uri(&'a self) -> Option<URI<'a>> {
136        if let Some(position) = self.iter().position(|c| *c == b':') {
137            let scheme = &self[..position];
138
139            let mut query: Option<&'a [u8]> = None;
140            let mut fragment: Option<&'a [u8]> = None;
141
142            let hierarchical_part_and_query_and_fragment = &self[position + 1..];
143
144            let hierarchical_part_and_query: &'a [u8];
145
146            if let Some(begin_of_fragment) = hierarchical_part_and_query_and_fragment
147                .iter()
148                .position(|c| *c == b'#')
149            {
150                hierarchical_part_and_query =
151                    &hierarchical_part_and_query_and_fragment[..begin_of_fragment];
152                fragment = Some(&hierarchical_part_and_query_and_fragment[begin_of_fragment + 1..]);
153            } else {
154                hierarchical_part_and_query = hierarchical_part_and_query_and_fragment;
155            }
156
157            let hierarchical_part: &'a [u8];
158
159            if let Some(begin_of_query) =
160                hierarchical_part_and_query.iter().position(|c| *c == b'?')
161            {
162                hierarchical_part = &hierarchical_part_and_query[..begin_of_query];
163                query = Some(&hierarchical_part_and_query[begin_of_query + 1..]);
164            } else {
165                hierarchical_part = hierarchical_part_and_query
166            }
167
168            if hierarchical_part.starts_with(b"//") {
169                let part = &hierarchical_part[2..];
170                if let Some(begin_of_path) = part.iter().position(|c| *c == b'/') {
171                    let authority = &part[..begin_of_path];
172                    let path = Some(&part[begin_of_path + 1..]);
173                    return Some(URI::Standard(
174                        scheme,
175                        URIPath::AbEmpty(authority, path),
176                        query,
177                        fragment,
178                    ));
179                } else {
180                    return Some(URI::Standard(
181                        scheme,
182                        URIPath::AbEmpty(part, None),
183                        query,
184                        fragment,
185                    ));
186                }
187            } else if hierarchical_part.starts_with(b"/") {
188                let segments = &hierarchical_part[1..];
189                return Some(URI::Standard(
190                    scheme,
191                    URIPath::Absolute(segments),
192                    query,
193                    fragment,
194                ));
195            } else if hierarchical_part.len() > 0 {
196                return Some(URI::Standard(
197                    scheme,
198                    URIPath::Rootless(hierarchical_part),
199                    query,
200                    fragment,
201                ));
202            } else if hierarchical_part.len() == 0 {
203                return Some(URI::Standard(scheme, URIPath::Empty(), query, fragment));
204            }
205        }
206
207        None
208    }
209}