nourl/src/lib.rs
Rasmus Melchior Jacobsen ca61ad8b99 Initial commit
2023-01-30 09:48:57 +01:00

186 lines
5.0 KiB
Rust

#![no_std]
mod error;
pub use error::Error;
/// A parsed URL to extract different parts of the URL.
pub struct Url<'a> {
host: &'a str,
scheme: UrlScheme,
port: Option<u16>,
path: &'a str,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum UrlScheme {
/// HTTP scheme
HTTP,
/// HTTPS (HTTP + TLS) scheme
HTTPS,
/// MQTT scheme
MQTT,
/// MQTTS (MQTT + TLS) scheme
MQTTS,
}
impl UrlScheme {
/// Get the default port for scheme
pub const fn default_port(&self) -> u16 {
match self {
UrlScheme::HTTP => 80,
UrlScheme::HTTPS => 443,
UrlScheme::MQTT => 1883,
UrlScheme::MQTTS => 8883,
}
}
}
impl<'a> Url<'a> {
/// Parse the provided url
pub fn parse(url: &'a str) -> Result<Url<'a>, Error> {
let mut parts = url.split("://");
let scheme = parts.next().unwrap();
let host_port_path = parts.next().ok_or(Error::NoScheme)?;
let scheme = if scheme.eq_ignore_ascii_case("http") {
Ok(UrlScheme::HTTP)
} else if scheme.eq_ignore_ascii_case("https") {
Ok(UrlScheme::HTTPS)
} else {
Err(Error::UnsupportedScheme)
}?;
let (host, port, path) = if let Some(port_delim) = host_port_path.find(':') {
// Port is defined
let host = &host_port_path[..port_delim];
let rest = &host_port_path[port_delim..];
let (port, path) = if let Some(path_delim) = rest.find('/') {
let port = rest[1..path_delim].parse::<u16>().ok();
let path = &rest[path_delim..];
let path = if path.is_empty() { "/" } else { path };
(port, path)
} else {
let port = rest[1..].parse::<u16>().ok();
(port, "/")
};
(host, port, path)
} else {
let (host, path) = if let Some(needle) = host_port_path.find('/') {
let host = &host_port_path[..needle];
let path = &host_port_path[needle..];
(host, if path.is_empty() { "/" } else { path })
} else {
(host_port_path, "/")
};
(host, None, path)
};
Ok(Self {
scheme,
host,
path,
port,
})
}
/// Get the url scheme
pub fn scheme(&self) -> UrlScheme {
self.scheme
}
/// Get the url host
pub fn host(&self) -> &'a str {
self.host
}
/// Get the url port if specified
pub fn port(&self) -> Option<u16> {
self.port
}
/// Get the url port or the default port for the scheme
pub fn port_or_default(&self) -> u16 {
self.port.unwrap_or_else(|| self.scheme.default_port())
}
/// Get the url path
pub fn path(&self) -> &'a str {
self.path
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
#[test]
fn test_parse_no_scheme() {
assert_eq!(Error::NoScheme, Url::parse("").err().unwrap());
assert_eq!(Error::NoScheme, Url::parse("http:/").err().unwrap());
}
#[test]
fn test_parse_unsupported_scheme() {
assert_eq!(
Error::UnsupportedScheme,
Url::parse("something://").err().unwrap()
);
}
#[test]
fn test_parse_no_host() {
let url = Url::parse("http://").unwrap();
assert_eq!(url.scheme(), UrlScheme::HTTP);
assert_eq!(url.host(), "");
assert_eq!(url.port_or_default(), 80);
assert_eq!(url.path(), "/");
}
#[test]
fn test_parse_minimal() {
let url = Url::parse("http://localhost").unwrap();
assert_eq!(url.scheme(), UrlScheme::HTTP);
assert_eq!(url.host(), "localhost");
assert_eq!(url.port_or_default(), 80);
assert_eq!(url.path(), "/");
}
#[test]
fn test_parse_path() {
let url = Url::parse("http://localhost/foo/bar").unwrap();
assert_eq!(url.scheme(), UrlScheme::HTTP);
assert_eq!(url.host(), "localhost");
assert_eq!(url.port_or_default(), 80);
assert_eq!(url.path(), "/foo/bar");
}
#[test]
fn test_parse_port() {
let url = Url::parse("http://localhost:8088").unwrap();
assert_eq!(url.scheme(), UrlScheme::HTTP);
assert_eq!(url.host(), "localhost");
assert_eq!(url.port().unwrap(), 8088);
assert_eq!(url.path(), "/");
}
#[test]
fn test_parse_port_path() {
let url = Url::parse("http://localhost:8088/foo/bar").unwrap();
assert_eq!(url.scheme(), UrlScheme::HTTP);
assert_eq!(url.host(), "localhost");
assert_eq!(url.port().unwrap(), 8088);
assert_eq!(url.path(), "/foo/bar");
}
#[test]
fn test_parse_scheme() {
let url = Url::parse("https://localhost/").unwrap();
assert_eq!(url.scheme(), UrlScheme::HTTPS);
assert_eq!(url.host(), "localhost");
assert_eq!(url.port_or_default(), 443);
assert_eq!(url.path(), "/");
}
}