Skip to main content

rust_web_server/url/path/
mod.rs

1use 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                    // read until first char of static pattern
190                    // add to map
191                    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                            // read the rest of static pattern
198                            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                    // read the rest of static pattern
210                }
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                // continue, unless the part is last,
216                // if so read to the end of path and add to map
217                if number_of_parts == index + 1 {
218                    // let mut buffer = vec![];
219
220                    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}