use super::SearchParams;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Url {
inner: url::Url,
}
impl Url {
pub fn parse(input: &str) -> Option<Url> {
url::Url::parse(input).ok().map(|inner| Url { inner })
}
pub fn parse_with_base(input: &str, base: &str) -> Option<Url> {
let base = url::Url::parse(base).ok()?;
if let Ok(inner) = url::Url::options().base_url(Some(&base)).parse(input) {
return Some(Url { inner });
}
let trimmed = input.trim_start_matches(|c: char| c <= ' ');
let special = matches!(base.scheme(), "http" | "https" | "ws" | "wss" | "ftp" | "file");
if special && trimmed.starts_with(['/', '\\']) {
return Url::parse(&format!("{}:{trimmed}", base.scheme()));
}
None
}
pub fn can_parse(input: &str) -> bool {
Url::parse(input).is_some()
}
pub fn href(&self) -> String {
self.inner.as_str().to_string()
}
pub fn protocol(&self) -> String {
format!("{}:", self.inner.scheme())
}
pub fn username(&self) -> String {
self.inner.username().to_string()
}
pub fn password(&self) -> String {
self.inner.password().unwrap_or("").to_string()
}
pub fn hostname(&self) -> String {
self.inner.host_str().unwrap_or("").to_string()
}
pub fn port(&self) -> String {
self.inner.port().map(|p| p.to_string()).unwrap_or_default()
}
pub fn host(&self) -> String {
match self.inner.port() {
Some(p) => format!("{}:{p}", self.hostname()),
None => self.hostname(),
}
}
pub fn origin(&self) -> String {
self.inner.origin().ascii_serialization()
}
pub fn pathname(&self) -> String {
self.inner.path().to_string()
}
pub fn search(&self) -> String {
match self.inner.query() {
Some(q) if !q.is_empty() => format!("?{q}"),
_ => String::new(),
}
}
pub fn hash(&self) -> String {
match self.inner.fragment() {
Some(f) if !f.is_empty() => format!("#{f}"),
_ => String::new(),
}
}
pub fn search_params(&self) -> SearchParams {
SearchParams::parse(self.inner.query().unwrap_or(""))
}
pub fn set_search_params(&mut self, params: &SearchParams) {
let text = params.to_string();
self.inner.set_query(if text.is_empty() { None } else { Some(&text) });
}
pub fn set_search(&mut self, text: &str) {
let text = text.strip_prefix('?').unwrap_or(text);
self.inner.set_query(if text.is_empty() { None } else { Some(text) });
}
pub fn set_pathname(&mut self, path: &str) {
self.inner.set_path(path);
}
pub fn set_hash(&mut self, text: &str) {
let text = text.strip_prefix('#').unwrap_or(text);
self.inner.set_fragment(if text.is_empty() { None } else { Some(text) });
}
pub fn set_hostname(&mut self, host: &str) -> bool {
self.inner.set_host(Some(host)).is_ok()
}
pub fn set_protocol(&mut self, scheme: &str) -> bool {
self.inner.set_scheme(scheme.trim_end_matches(':')).is_ok()
}
}
impl std::fmt::Display for Url {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.inner.as_str())
}
}