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}