rust_web_server/url/path/
mod.rs1use std::collections::HashMap;
2use std::fmt::{Display, Formatter};
3use crate::symbol::SYMBOL;
4
5#[cfg(test)]
6mod tests;
7
8pub struct UrlPath;
9
10#[derive(Clone, Debug)]
11pub struct Part {
12 pub is_static: bool,
13 pub name: Option<String>,
14 pub value: Option<String>,
15 pub static_pattern: Option<String>
16}
17
18impl Display for Part {
19 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
20 write!(
21 f,
22 "{:?} {:?} {:?} {:?}",
23 &self.is_static, &self.name, &self.value, &self.static_pattern
24 )
25 }
26}
27
28impl UrlPath {
29 pub fn extract_parts_from_pattern(_pattern: &str) -> Result<Vec<Part>, String>{
30 let mut part_list: Vec<Part> = vec![];
31 let mut _buffer: Vec<char> = vec![];
32 let mut previous_char: Option<char> = None;
33 let mut is_opened_token = false;
34
35 for _char in _pattern.chars() {
36 if _char.is_whitespace() || _char.is_control() {
37 return Err("path contains control character or whitespace".to_string())
38 }
39
40 _buffer.push(_char);
41
42 if _char == '[' && previous_char.is_some() && previous_char.unwrap() == '[' {
43 if is_opened_token {
44 return Err("at least one extra [ char".to_string());
45 }
46 is_opened_token = true;
47 if _buffer.len() != 0 && _buffer.len() >= 2 {
48 let without_square_brackets = _buffer.len() - 2;
49 let pattern : String = _buffer[0..without_square_brackets].into_iter().collect();
50 if pattern.len() > 0 {
51 let part = Part {
52 is_static: true,
53 name: None,
54 value: None,
55 static_pattern: Some(pattern),
56 };
57 part_list.push(part);
58 }
59 }
60 _buffer = vec![];
61
62 let previous_part_is_token = part_list.last().is_some() && part_list.last().unwrap().is_static == false;
63 if previous_part_is_token {
64 return Err("two consecutive tokens one after another".to_string())
65 }
66 }
67
68 if _char == ']' && previous_char.is_some() && previous_char.unwrap() == ']' {
69 is_opened_token = false;
70 let without_square_brackets = _buffer.len() - 2;
71 let key : String = _buffer[0..without_square_brackets].into_iter().collect();
72 let part = Part {
73 is_static: false,
74 name: Some(key),
75 value: None,
76 static_pattern: None,
77 };
78 part_list.push(part);
79
80 _buffer = vec![];
81
82 }
83
84 previous_char = Some(_char.clone());
85 }
86
87 if _buffer.len() != 0 {
88 let static_ending : String = _buffer.into_iter().collect();
89
90 let part = Part {
91 is_static: true,
92 name: None,
93 value: None,
94 static_pattern: Some(static_ending),
95 };
96 part_list.push(part);
97 _buffer = vec![];
98 }
99
100 Ok(part_list)
101 }
102
103 pub fn is_matching(_path: &str, _pattern: &str) -> Result<bool, String> {
104 let is_matching = true;
105 let is_not_matching = false;
106
107 for char in _path.chars() {
108 if char.is_whitespace() || char.is_control() {
109 return Err("path contains control character or whitespace".to_string());
110 }
111 }
112
113 let boxed_parts = UrlPath::extract_parts_from_pattern(_pattern);
114 if boxed_parts.is_err() {
115 return Err(boxed_parts.err().unwrap());
116 }
117
118 let parts : Vec<Part> = boxed_parts.unwrap();
119 let mut populated_parts : Vec<Part> = vec![];
120
121 let mut url_path = _path.to_string();
122 let number_of_parts = parts.len();
123 for (index, part) in parts.iter().enumerate() {
124 let part = part;
125 let is_there_next_part = index < number_of_parts - 1;
126 if part.is_static {
127 let static_pattern = part.static_pattern.clone().unwrap();
128 if !url_path.starts_with(&static_pattern) {
129 return Ok(is_not_matching)
130 }
131
132 let _part = part.clone();
133 populated_parts.push(_part);
134
135 url_path = url_path.replacen(static_pattern.as_str(), SYMBOL.empty_string, 1);
136 } else {
137 if !is_there_next_part {
138 let _part = Part {
139 is_static: false,
140 name: part.name.clone(),
141 value: Some(url_path.clone()),
142 static_pattern: None,
143 };
144 let _part = part.clone();
145 populated_parts.push(_part);
146
147 } else {
148 let next_part = parts.get(index + 1).unwrap();
149 let delimiter = next_part.static_pattern.clone().unwrap().chars().next().unwrap();
150 let occurence = url_path.find(delimiter);
151 if occurence.is_none() {
152 return Ok(is_not_matching)
153 }
154 let occurence_place = occurence.unwrap();
155 let token = url_path[..occurence_place].to_string();
156 let _part = Part {
157 is_static: false,
158 name: part.name.clone(),
159 value: Some(token.clone()),
160 static_pattern: None,
161 };
162 populated_parts.push(_part.clone());
163
164 url_path = url_path.chars().skip(occurence_place).collect();
165 }
166 }
167 }
168
169 Ok(is_matching)
170 }
171
172 pub fn extract(_path: &str, _pattern: &str) -> Result<HashMap<String, String>, String> {
173 let boxed_parts = UrlPath::extract_parts_from_pattern(_pattern);
174 if boxed_parts.is_err() {
175 return Err(boxed_parts.err().unwrap());
176 }
177
178 let parts : Vec<Part> = boxed_parts.unwrap();
179 let mut resulting_parts : Vec<Part> = vec![];
180
181 let mut path = _path.to_string();
182 let mut previous_part: Option<Part> = None;
183 let number_of_parts = parts.len();
184 for (index, part) in parts.iter().enumerate() {
185
186 if part.is_static {
187 if previous_part.is_some() {
188
189 let static_pattern = part.static_pattern.clone().unwrap();
192 let first_char_to_stop = static_pattern.chars().next().unwrap();
193 let mut buffer = vec![];
194
195 for char in path.clone().chars() {
196 if char == first_char_to_stop {
197 break;
199 } else {
200 let removed_char = path.remove(0);
201 buffer.push(removed_char);
202 }
203 }
204 let token : String = buffer.iter().collect();
205 let mut processed_part = previous_part.clone().unwrap();
206 processed_part.value = Some(token);
207 resulting_parts.push(processed_part.clone());
208 } else {
209 }
211
212 let static_pattern = part.static_pattern.clone().unwrap();
213 path = path.strip_prefix(static_pattern.as_str()).unwrap().to_string();
214 } else {
215 if number_of_parts == index + 1 {
218 let mut processed_part = part.clone();
221 processed_part.value = Some(path.clone());
222 resulting_parts.push(processed_part.clone());
223 }
224
225 }
226
227 previous_part = Some(part.clone());
228 }
229
230 let mut map = HashMap::new();
231 for part in resulting_parts {
232 let key = part.name.unwrap();
233 let value = part.value.unwrap();
234
235 map.insert(key, value);
236 }
237
238 Ok(map)
239 }
240
241 pub fn build(_params: HashMap<String, String>, _pattern: &str) -> Result<String, String> {
242 let boxed_parts = UrlPath::extract_parts_from_pattern(_pattern);
243 if boxed_parts.is_err() {
244 return Err(boxed_parts.err().unwrap());
245 }
246
247 let parts : Vec<Part> = boxed_parts.unwrap();
248
249
250 let mut strings_array = vec![];
251 for part in parts {
252 if part.is_static {
253 strings_array.push(part.static_pattern.unwrap())
254 } else {
255 let key = part.name.unwrap().to_string();
256 let boxed_value = _params.get(key.as_str());
257 if boxed_value.is_none() {
258 return Err(format!("specified parameter {} is not found", key))
259 }
260 let value = boxed_value.unwrap();
261 strings_array.push(value.clone());
262 }
263 }
264 let populated_pattern : String = strings_array.join(SYMBOL.empty_string);
265
266 Ok(populated_pattern)
267
268 }
269}