use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Url {
#[allow(dead_code)]
pub url: String,
#[allow(dead_code)]
pub scheme: String,
pub host: String,
pub port: u16,
pub path_query: String,
#[allow(dead_code)]
pub path: String,
query: String,
query_params: HashMap<String, String>,
}
impl Url {
pub fn parse(url: &str) -> Result<Self, String> {
let uri = url.to_string();
let (uri, query) = match uri.find("?") {
Some(idx) => (&uri[..idx], &uri[idx + 1..]),
None => (uri.as_str(), ""),
};
let scheme = match uri.find(":") {
None => return Err("Invalid Scheme Not Found".to_string()),
Some(e) => uri[..e].to_string(),
};
let uri = uri
.trim_start_matches(scheme.as_str())
.trim_start_matches(":")
.trim_start_matches("//");
let (host_port, path) = match uri.find("/") {
Some(idx) => (&uri[..idx], &uri[idx..]),
None => (uri, "/"),
};
let path_query = if query.is_empty() {
path.to_string()
} else {
format!("{}?{}", path, query)
};
let (host, port) = match host_port.rsplit_once(':') {
Some((h, p_str)) if !h.is_empty() && !p_str.is_empty() => {
let port: u16 = p_str.parse().unwrap_or_default();
(h.to_string(), port)
}
_ => {
let default_port = if scheme == "https" { 443 } else { 80 };
(host_port.to_string(), default_port)
}
};
let query_params = if !query.is_empty() {
let mut query_params = HashMap::new();
let r = query.split('&').collect::<Vec<&str>>();
for item in r {
let t = item.split('=').collect::<Vec<&str>>();
query_params.insert(t[0].to_string(), t[1].to_string());
}
query_params
} else {
HashMap::new()
};
Ok(Self {
url: url.to_string(),
scheme,
host,
port,
path_query: path_query.to_string(),
path: path.to_string(),
query: query.to_string(),
query_params,
})
}
pub fn set_query(&mut self, param: HashMap<String, String>) {
if self.query.is_empty() {
self.query_params = param;
} else {
self.query_params.extend(param);
}
self.update_query();
}
fn update_query(&mut self) {
self.query = self
.query_params
.iter()
.map(|(key, value)| format!("{}={}", key, value))
.collect::<Vec<String>>()
.join("&");
self.path_query = match self.path_query.find("?") {
None => format!("{}?{}", self.path_query, self.query),
Some(n) => format!("{}?{}", &self.path_query[..n], self.query),
}
}
}
#[test]
fn test() {
let test = Url::parse("http://127.0.0.1:8080/ttt/index.html?ttt=1111").unwrap();
println!("scheme: {}", test.scheme);
println!("host: {}", test.host);
println!("port: {}", test.port);
println!("path: {}", test.path);
println!("query: {}", test.query);
println!("query_params: {:?}", test.query_params);
let test = Url::parse("http://www.exe.com/ttt/index.html?ttt=1111").unwrap();
println!("scheme: {}", test.scheme);
println!("host: {}", test.host);
println!("port: {}", test.port);
println!("path: {}", test.path);
println!("query: {}", test.query);
println!("query_params: {:?}", test.query_params);
}