Skip to content

Commit 116659a

Browse files
committed
add: Host/Hostname structs in python
1 parent e215df2 commit 116659a

File tree

1 file changed

+115
-3
lines changed

1 file changed

+115
-3
lines changed

python/src/lib.rs

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::net::IpAddr;
2+
13
use pyo3::{exceptions::PyValueError, prelude::*, types::PyDict};
24

35
struct Error(faup_rs::Error);
@@ -14,6 +16,113 @@ impl From<Error> for PyErr {
1416
}
1517
}
1618

19+
#[pyclass]
20+
#[derive(Clone)]
21+
pub struct Hostname {
22+
hostname: String,
23+
#[pyo3(get)]
24+
subdomain: Option<String>,
25+
#[pyo3(get)]
26+
domain: Option<String>,
27+
#[pyo3(get)]
28+
suffix: Option<String>,
29+
}
30+
31+
impl From<faup_rs::Hostname<'_>> for Hostname {
32+
fn from(value: faup_rs::Hostname<'_>) -> Self {
33+
Self {
34+
hostname: value.full_name().to_string(),
35+
subdomain: value.subdomain().map(|s| s.into()),
36+
domain: value.domain().map(|s| s.into()),
37+
suffix: value.suffix().map(|s| s.into()),
38+
}
39+
}
40+
}
41+
42+
#[pymethods]
43+
impl Hostname {
44+
#[new]
45+
pub fn new(hn: &str) -> PyResult<Self> {
46+
let h = faup_rs::Host::parse(hn).map_err(|e| PyValueError::new_err(e.to_string()))?;
47+
match h {
48+
faup_rs::Host::Hostname(h) => Ok(h.into()),
49+
faup_rs::Host::Ip(_) => Err(PyValueError::new_err("invalid hostname")),
50+
}
51+
}
52+
53+
pub fn __str__(&self) -> &str {
54+
self.hostname.as_str()
55+
}
56+
}
57+
58+
#[pyclass]
59+
pub enum Host {
60+
/// A hostname (domain name).
61+
Hostname(Hostname),
62+
/// An IP address (either IPv4 or IPv6).
63+
Ip(IpAddr),
64+
}
65+
66+
impl From<faup_rs::Host<'_>> for Host {
67+
fn from(value: faup_rs::Host) -> Self {
68+
match value {
69+
faup_rs::Host::Hostname(h) => Host::Hostname(h.into()),
70+
faup_rs::Host::Ip(ip) => Host::Ip(ip),
71+
}
72+
}
73+
}
74+
75+
#[pymethods]
76+
impl Host {
77+
#[new]
78+
pub fn new(s: &str) -> PyResult<Self> {
79+
faup_rs::Host::parse(s)
80+
.map(Host::from)
81+
.map_err(|e| PyValueError::new_err(e.to_string()))
82+
}
83+
84+
pub fn try_into_hostname(&self) -> PyResult<Hostname> {
85+
match self {
86+
Host::Hostname(h) => Ok(h.clone()),
87+
Host::Ip(_) => Err(PyValueError::new_err("host object is not a hostname")),
88+
}
89+
}
90+
91+
pub fn try_into_ip(&self) -> PyResult<String> {
92+
match self {
93+
Host::Hostname(_) => Err(PyValueError::new_err("host object is not an ip address")),
94+
Host::Ip(ip) => Ok(ip.to_string()),
95+
}
96+
}
97+
98+
#[inline(always)]
99+
pub fn is_hostname(&self) -> bool {
100+
matches!(self, Host::Hostname(_))
101+
}
102+
103+
#[inline(always)]
104+
pub fn is_ipv4(&self) -> bool {
105+
matches!(self, Host::Ip(IpAddr::V4(_)))
106+
}
107+
108+
#[inline(always)]
109+
pub fn is_ipv6(&self) -> bool {
110+
matches!(self, Host::Ip(IpAddr::V6(_)))
111+
}
112+
113+
#[inline(always)]
114+
pub fn is_ip_addr(&self) -> bool {
115+
self.is_ipv4() | self.is_ipv6()
116+
}
117+
118+
pub fn __str__(&self) -> String {
119+
match self {
120+
Self::Hostname(h) => h.hostname.clone(),
121+
Self::Ip(ip) => ip.to_string(),
122+
}
123+
}
124+
}
125+
17126
/// A parsed URL representation for Python.
18127
///
19128
/// This class provides access to all components of a parsed URL, including scheme,
@@ -144,6 +253,9 @@ impl Url {
144253
.map_err(|e| PyValueError::new_err(e.to_string()))
145254
}
146255

256+
pub fn __str__(&self) -> &str {
257+
self.orig.as_ref()
258+
}
147259
}
148260

149261
/// A compatibility class that mimics the FAUP (Fast URL Parser) Python API.
@@ -284,12 +396,10 @@ impl FaupCompat {
284396
domain
285397
.strip_suffix(tld)
286398
.and_then(|dom| dom.strip_suffix('.'))
287-
}
288-
else {
399+
} else {
289400
None
290401
}
291402
}
292-
293403
}
294404

295405
/// A Python module implemented in Rust for URL parsing.
@@ -312,6 +422,8 @@ impl FaupCompat {
312422
#[pymodule]
313423
fn pyfaup(m: &Bound<'_, PyModule>) -> PyResult<()> {
314424
m.add_class::<Url>()?;
425+
m.add_class::<Host>()?;
426+
m.add_class::<Hostname>()?;
315427
m.add_class::<FaupCompat>()?;
316428

317429
Ok(())

0 commit comments

Comments
 (0)