fetch_js/
url.rs

1use std::collections::HashMap;
2use std::fmt::Display;
3
4#[derive(Debug)]
5pub struct URL {
6  hash: String,
7  pathname: String,
8  port: String,
9  protocol: String,
10  username: String,
11  password: String,
12  hostname: String,
13  pub search_params: URLSearchParams,
14}
15
16impl URL {
17  fn parse_protocol(url: &mut String) -> Result<String, Box<dyn std::error::Error>> {
18    let protocol_end = url.find("//").ok_or("Invalid URL")?;
19    let protocol = &url.clone()[..protocol_end];
20    *url = url[protocol_end + 2..].to_string();
21    Ok(protocol.to_string())
22  }
23  
24  fn parse_username_and_password(url: &mut String) -> Result<(String, String), Box<dyn std::error::Error>> {
25    let username_end = url.find(":").ok_or("Invalid URL")?;
26    let username = &url.clone()[..username_end];
27    *url = url[username_end + 1..].to_string();
28    let password_end = url.find("@").ok_or("Invalid URL")?;
29    let password = &url.clone()[..password_end];
30    *url = url[password_end + 1..].to_string();
31    Ok((username.to_string(), password.to_string()))
32  }
33  
34  fn parse_hostname_and_port(url: &mut String) -> Result<(String, String), Box<dyn std::error::Error>> {
35    let hostname_end = url.find(":").ok_or("Invalid URL")?;
36    let hostname = &url.clone()[..hostname_end];
37    *url = url[hostname_end + 1..].to_string();
38    let port_end = url.find("/").ok_or("Invalid URL")?;
39    let port = &url.clone()[..port_end];
40    *url = url[port_end + 1..].to_string();
41    Ok((hostname.to_string(), port.to_string()))
42  }
43  
44  fn parse_pathname(url: &mut String) -> Result<String, Box<dyn std::error::Error>> {
45    let pathname_end = url.find("?").ok_or("Invalid URL")?;
46    let pathname = &url.clone()[..pathname_end];
47    *url = url[pathname_end + 1..].to_string();
48    Ok(pathname.to_string())
49  }
50  
51  fn parse_search_params_and_hash(url: &mut String) -> Result<(URLSearchParams, String), Box<dyn std::error::Error>> {
52    let search_end = url.find("#").ok_or("Invalid URL")?;
53    let search = &url.clone()[..search_end];
54    *url = url[search_end + 1..].to_string();
55    let hash = url.clone();
56    Ok((URLSearchParams::new(search), hash.to_string()))
57  }
58  
59  pub fn new(url: &str) -> Result<Self, Box<dyn std::error::Error>> {
60    let mut url = url.to_string();
61    let protocol = Self::parse_protocol(&mut url).unwrap_or_else(|_| "http".to_string());
62    let (username, password) = Self::parse_username_and_password(&mut url).unwrap_or_else(|_| ("".to_string(), "".to_string()));
63    let (hostname, port) = Self::parse_hostname_and_port(&mut url).unwrap_or_else(|_| ("".to_string(), "".to_string()));
64    let pathname = Self::parse_pathname(&mut url).unwrap_or_else(|_| "".to_string());
65    let (search_params, hash) = Self::parse_search_params_and_hash(&mut url).unwrap_or_else(|_| (URLSearchParams::new(""), "".to_string()));
66    Ok(Self {
67      hash,
68      pathname,
69      port,
70      protocol,
71      username,
72      password,
73      hostname,
74      search_params, 
75    })
76  }
77}
78
79impl URL {
80  pub fn get_protocol(&self) -> String {
81    self.protocol.clone()
82  }
83  
84  pub fn set_protocol(&mut self, protocol: &str) {
85    self.protocol = protocol.to_string();
86  }
87  
88  pub fn get_username(&self) -> String {
89    self.username.clone()
90  }
91  
92  pub fn set_username(&mut self, username: &str) {
93    self.username = username.to_string();
94  }
95  
96  pub fn get_password(&self) -> String {
97    self.password.clone()
98  }
99  
100  pub fn set_password(&mut self, password: &str) {
101    self.password = password.to_string();
102  }
103  
104  pub fn get_hostname(&self) -> String {
105    self.hostname.clone()
106  }
107  
108  pub fn set_hostname(&mut self, hostname: &str) {
109    self.hostname = hostname.to_string();
110  }
111  
112  pub fn get_port(&self) -> String {
113    self.port.clone()
114  }
115  
116  pub fn set_port(&mut self, port: &str) {
117    self.port = port.to_string();
118  }
119  
120  pub fn get_pathname(&self) -> String {
121    self.pathname.clone()
122  }
123  
124  pub fn set_pathname(&mut self, pathname: &str) {
125    self.pathname = pathname.to_string();
126  }
127  
128  pub fn get_hash(&self) -> String {
129    self.hash.clone()
130  }
131  
132  pub fn set_hash(&mut self, hash: &str) {
133    self.hash = hash.to_string();
134  }
135}
136
137impl URL {
138  pub fn get_href(&self) -> String {
139    format!("{}://{}:{}{}?{}#{}", self.protocol, self.hostname, self.port, self.pathname, self.search_params, self.hash)
140  }
141}
142
143#[derive(Debug)]
144pub struct URLSearchParams {
145  params: HashMap<String, String>,
146}
147
148impl URLSearchParams {
149  pub fn new(param_str: &str) -> Self {
150    let param_strings = param_str.split('&').collect::<Vec<&str>>();
151    let mut params = HashMap::new();
152    for param in param_strings {
153      let parts = param.split('=').collect::<Vec<&str>>();
154      params.insert(parts[0].to_string(), parts[1].to_string());
155    };
156    Self {
157      params,
158    }
159  }
160}
161
162impl URLSearchParams {
163  pub fn get(&self, name: &str) -> Option<String> {
164    self.params.get(name).map(|v| v.to_string())
165  }
166
167  pub fn set(&mut self, name: &str, value: &str) {
168    self.params.insert(name.to_string(), value.to_string());
169  }
170
171  pub fn has(&self, name: &str) -> bool {
172    self.params.contains_key(name)
173  }
174
175  pub fn delete(&mut self, name: &str) {
176    self.params.remove(name);
177  }
178
179  pub fn entries(&self) -> SearchParamsIter {
180    SearchParamsIter::new(self.params.clone())
181  }
182
183  pub fn for_each<F>(&self, callback: F) where F: Fn(&str, &str, &Self) {
184    for (k, v) in self.params.iter() {
185      callback(k, v, self);
186    }
187  }
188
189  pub fn keys(&self) -> StringIter {
190    StringIter::new(self.params.keys().map(|k| k.to_string()).collect())
191  }
192
193  pub fn values(&self) -> StringIter {
194    StringIter::new(self.params.values().map(|v| v.to_string()).collect())
195  }
196}
197
198impl IntoIterator for URLSearchParams {
199  type Item = (String, String);
200  type IntoIter = SearchParamsIter;
201  fn into_iter(self) -> Self::IntoIter {
202    SearchParamsIter::new(self.params)
203  }
204}
205
206impl Display for URLSearchParams {
207  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208    let mut param_strings = vec![];
209    for (k, v) in self.params.iter() {
210      param_strings.push(format!("{}={}", k, v));
211    };
212    write!(f, "{}", param_strings.join("&"))
213  }
214}
215
216#[derive(Debug)]
217pub struct SearchParamsIter {
218  params: Vec<(String, String)>,
219  index: usize,
220}
221
222impl SearchParamsIter {
223  pub fn new(params: HashMap<String, String>) -> Self {
224    let mut param_vec = vec![];
225    for (k, v) in params.iter() {
226      param_vec.push((k.to_string(), v.to_string()));
227    };
228    Self {
229      params: param_vec,
230      index: 0,
231    }
232  }
233}
234
235impl Iterator for SearchParamsIter {
236  type Item = (String, String);
237  fn next(&mut self) -> Option<Self::Item> {
238    if self.index < self.params.len() {
239      let param = self.params[self.index].clone();
240      self.index += 1;
241      Some(param)
242    } else {
243      None
244    }
245  }
246}
247
248#[derive(Debug)]
249pub struct StringIter {
250  strings: Vec<String>,
251  index: usize,
252}
253
254impl StringIter {
255  pub fn new(strings: Vec<String>) -> Self {
256    Self {
257      strings,
258      index: 0,
259    }
260  }
261}
262
263impl Iterator for StringIter {
264  type Item = String;
265  fn next(&mut self) -> Option<Self::Item> {
266    if self.index < self.strings.len() {
267      let string = self.strings[self.index].clone();
268      self.index += 1;
269      Some(string)
270    } else {
271      None
272    }
273  }
274}