mirror of
https://github.com/Hopiu/lychee.git
synced 2026-04-06 22:40:58 +00:00
120 lines
3.2 KiB
Rust
120 lines
3.2 KiB
Rust
use anyhow::{bail, Result};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::net::IpAddr;
|
|
use std::{convert::TryFrom, fmt::Display};
|
|
use url::Url;
|
|
|
|
/// Lychee's own representation of a URI, which encapsulates all support formats
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum Uri {
|
|
/// Website URL
|
|
Website(Url),
|
|
/// Mail address
|
|
Mail(String),
|
|
}
|
|
|
|
impl Uri {
|
|
pub fn as_str(&self) -> &str {
|
|
match self {
|
|
Uri::Website(url) => url.as_str(),
|
|
Uri::Mail(address) => address.as_str(),
|
|
}
|
|
}
|
|
|
|
pub fn scheme(&self) -> Option<String> {
|
|
match self {
|
|
Uri::Website(url) => Some(url.scheme().to_string()),
|
|
Uri::Mail(_address) => None,
|
|
}
|
|
}
|
|
|
|
pub fn host_ip(&self) -> Option<IpAddr> {
|
|
match self {
|
|
Self::Website(url) => match url.host()? {
|
|
url::Host::Ipv4(v4_addr) => Some(v4_addr.into()),
|
|
url::Host::Ipv6(v6_addr) => Some(v6_addr.into()),
|
|
_ => None,
|
|
},
|
|
Self::Mail(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&str> for Uri {
|
|
type Error = anyhow::Error;
|
|
|
|
fn try_from(s: &str) -> Result<Self> {
|
|
// Remove the `mailto` scheme if it exists
|
|
// to avoid parsing it as a website URL.
|
|
let s = s.trim_start_matches("mailto:");
|
|
if let Ok(uri) = Url::parse(s) {
|
|
return Ok(Uri::Website(uri));
|
|
};
|
|
if s.contains('@') {
|
|
return Ok(Uri::Mail(s.to_string()));
|
|
};
|
|
bail!("Cannot convert to Uri")
|
|
}
|
|
}
|
|
|
|
impl Display for Uri {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.as_str())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::test_utils::website;
|
|
|
|
use super::*;
|
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
|
|
|
#[test]
|
|
fn test_uri_from_str() {
|
|
assert!(matches!(Uri::try_from(""), Err(_)));
|
|
assert_eq!(
|
|
Uri::try_from("http://example.org").unwrap(),
|
|
website("http://example.org")
|
|
);
|
|
assert_eq!(
|
|
Uri::try_from("mail@example.org").unwrap(),
|
|
Uri::Mail("mail@example.org".to_string())
|
|
);
|
|
assert_eq!(
|
|
Uri::try_from("mailto:mail@example.org").unwrap(),
|
|
Uri::Mail("mail@example.org".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_uri_host_ip_v4() {
|
|
let uri = website("http://127.0.0.1");
|
|
let ip = uri.host_ip().expect("Expected a valid IPv4");
|
|
assert_eq!(ip, IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_uri_host_ip_v6() {
|
|
let uri = website("https://[2020::0010]");
|
|
let ip = uri.host_ip().expect("Expected a valid IPv6");
|
|
assert_eq!(
|
|
ip,
|
|
IpAddr::V6(Ipv6Addr::new(0x2020, 0, 0, 0, 0, 0, 0, 0x10))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_uri_host_ip_no_ip() {
|
|
let uri = website("https://some.cryptic/url");
|
|
let ip = uri.host_ip();
|
|
assert!(ip.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_mail() {
|
|
let uri = website("http://127.0.0.1");
|
|
let ip = uri.host_ip().expect("Expected a valid IPv4");
|
|
assert_eq!(ip, IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
|
|
}
|
|
}
|