use alloc::string::String;
use core::fmt;
use azul_css::{AzString, impl_result, impl_result_inner};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Url {
pub href: AzString,
pub scheme: AzString,
pub host: AzString,
pub port: u16,
pub path: AzString,
pub query: AzString,
pub fragment: AzString,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct UrlParseError {
pub message: AzString,
}
impl fmt::Display for UrlParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message.as_str())
}
}
#[cfg(feature = "std")]
impl std::error::Error for UrlParseError {}
impl_result!(
Url,
UrlParseError,
ResultUrlUrlParseError,
copy = false,
[Debug, Clone, PartialEq]
);
impl Url {
#[cfg(feature = "http")]
pub fn parse(s: &str) -> Result<Self, UrlParseError> {
use ::url::Url as UrlParser;
let parsed = UrlParser::parse(s)
.map_err(|e| UrlParseError {
message: AzString::from(e.to_string()),
})?;
Ok(Self {
href: AzString::from(parsed.as_str().to_string()),
scheme: AzString::from(parsed.scheme().to_string()),
host: AzString::from(parsed.host_str().unwrap_or("").to_string()),
port: parsed.port().unwrap_or(0),
path: AzString::from(parsed.path().to_string()),
query: AzString::from(parsed.query().unwrap_or("").to_string()),
fragment: AzString::from(parsed.fragment().unwrap_or("").to_string()),
})
}
pub fn from_parts(
scheme: &str,
host: &str,
port: u16,
path: &str,
) -> Self {
let port_str = if port == 0 || (scheme == "http" && port == 80) || (scheme == "https" && port == 443) {
String::new()
} else {
format!(":{}", port)
};
let href = format!("{}://{}{}{}", scheme, host, port_str, path);
Self {
href: AzString::from(href),
scheme: AzString::from(scheme.to_string()),
host: AzString::from(host.to_string()),
port,
path: AzString::from(path.to_string()),
query: AzString::from(String::new()),
fragment: AzString::from(String::new()),
}
}
pub fn as_str(&self) -> &str {
self.href.as_str()
}
pub fn is_https(&self) -> bool {
self.scheme.as_str() == "https"
}
pub fn is_http(&self) -> bool {
self.scheme.as_str() == "http"
}
pub fn effective_port(&self) -> u16 {
if self.port != 0 {
self.port
} else if self.is_https() {
443
} else if self.is_http() {
80
} else {
0
}
}
#[cfg(feature = "http")]
pub fn join(&self, path: &str) -> Result<Self, UrlParseError> {
use ::url::Url as UrlParser;
let base = UrlParser::parse(self.href.as_str())
.map_err(|e| UrlParseError {
message: AzString::from(e.to_string()),
})?;
let joined = base.join(path)
.map_err(|e| UrlParseError {
message: AzString::from(e.to_string()),
})?;
Self::parse(joined.as_str())
}
}
impl fmt::Display for Url {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.href.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "http")]
fn test_url_parse() {
let url = Url::parse("https://example.com:8080/path?query=1#frag").unwrap();
assert_eq!(url.scheme.as_str(), "https");
assert_eq!(url.host.as_str(), "example.com");
assert_eq!(url.port, 8080);
assert_eq!(url.path.as_str(), "/path");
assert_eq!(url.query.as_str(), "query=1");
assert_eq!(url.fragment.as_str(), "frag");
}
#[test]
fn test_url_from_parts() {
let url = Url::from_parts("https", "example.com", 443, "/api");
assert!(url.is_https());
assert_eq!(url.effective_port(), 443);
}
}