@@ -16,6 +16,23 @@ impl From<Error> for PyErr {
1616 }
1717}
1818
19+ /// Represents a parsed hostname (domain name) with subdomain, domain, and suffix components.
20+ ///
21+ /// # Attributes
22+ ///
23+ /// * `hostname` - `str` - The full hostname.
24+ /// * `subdomain` - `Optional[str]` - The subdomain part, if present.
25+ /// * `domain` - `Optional[str]` - The domain part, if present.
26+ /// * `suffix` - `Optional[str]` - The suffix (TLD) part, if present.
27+ ///
28+ /// # Example
29+ ///
30+ /// >>> from pyfaup import Hostname
31+ /// >>> hn = Hostname("sub.example.com")
32+ /// >>> print(hn.hostname) # "sub.example.com"
33+ /// >>> print(hn.subdomain) # "sub"
34+ /// >>> print(hn.domain) # "example"
35+ /// >>> print(hn.suffix) # "com"
1936#[ pyclass]
2037#[ derive( Clone ) ]
2138pub struct Hostname {
@@ -41,6 +58,30 @@ impl From<faup_rs::Hostname<'_>> for Hostname {
4158
4259#[ pymethods]
4360impl Hostname {
61+ /// Creates a new [`Hostname`] by parsing a hostname string.
62+ ///
63+ /// # Arguments
64+ ///
65+ /// * `hn` - `str` - The hostname string to parse.
66+ ///
67+ /// # Returns
68+ ///
69+ /// * [`Hostname`] - The parsed hostname.
70+ ///
71+ /// # Raises
72+ ///
73+ /// * [`ValueError`] - If the input is not a valid hostname.
74+ ///
75+ /// # Example
76+ ///
77+ /// >>> from pyfaup import Hostname
78+ /// >>> hn = Hostname("sub.example.com")
79+ /// >>> print(hn.hostname) # "sub.example.com"
80+ ///
81+ /// >>> Hostname("192.168.1.1")
82+ /// Traceback (most recent call last):
83+ /// ...
84+ /// ValueError: invalid hostname
4485 #[ new]
4586 pub fn new ( hn : & str ) -> PyResult < Self > {
4687 let h = faup_rs:: Host :: parse ( hn) . map_err ( |e| PyValueError :: new_err ( e. to_string ( ) ) ) ?;
@@ -55,6 +96,7 @@ impl Hostname {
5596 }
5697}
5798
99+ /// Represents a host, which can be either a [`Hostname`] or an [`IpAddr`].
58100#[ pyclass]
59101pub enum Host {
60102 /// A hostname (domain name).
@@ -74,42 +116,135 @@ impl From<faup_rs::Host<'_>> for Host {
74116
75117#[ pymethods]
76118impl Host {
119+ /// Creates a new [`Host`] by parsing a host string.
120+ ///
121+ /// # Arguments
122+ ///
123+ /// * `s` - `str` - The host string to parse.
124+ ///
125+ /// # Returns
126+ ///
127+ /// * [`Host`] - The parsed host.
128+ ///
129+ /// # Raises
130+ ///
131+ /// * [`ValueError`] - If the input is not a valid host.
132+ ///
133+ /// # Example
134+ ///
135+ /// >>> from pyfaup import Host
136+ /// >>> host = Host("sub.example.com")
137+ /// >>> print(host.is_hostname()) # True
138+ ///
139+ /// >>> Host("invalid host")
140+ /// Traceback (most recent call last):
141+ /// ...
142+ /// ValueError: ...
77143 #[ new]
78144 pub fn new ( s : & str ) -> PyResult < Self > {
79145 faup_rs:: Host :: parse ( s)
80146 . map ( Host :: from)
81147 . map_err ( |e| PyValueError :: new_err ( e. to_string ( ) ) )
82148 }
83149
150+ /// Attempts to convert the host into a [`Hostname`].
151+ ///
152+ /// # Returns
153+ ///
154+ /// * [`Hostname`] - The hostname.
155+ ///
156+ /// # Raises
157+ ///
158+ /// * [`ValueError`] - If the host is not a hostname.
159+ ///
160+ /// # Example
161+ ///
162+ /// >>> from pyfaup import Host
163+ /// >>> host = Host("sub.example.com")
164+ /// >>> hn = host.try_into_hostname()
165+ /// >>> print(hn.hostname) # "sub.example.com"
166+ ///
167+ /// >>> Host("192.168.1.1").try_into_hostname()
168+ /// Traceback (most recent call last):
169+ /// ...
170+ /// ValueError: host object is not a hostname
84171 pub fn try_into_hostname ( & self ) -> PyResult < Hostname > {
85172 match self {
86173 Host :: Hostname ( h) => Ok ( h. clone ( ) ) ,
87174 Host :: Ip ( _) => Err ( PyValueError :: new_err ( "host object is not a hostname" ) ) ,
88175 }
89176 }
90177
178+ /// Attempts to convert the host into an IP address string.
179+ ///
180+ /// # Returns
181+ ///
182+ /// * `str` - The IP address as a string.
183+ ///
184+ /// # Raises
185+ ///
186+ /// * [`ValueError`] - If the host is not an IP address.
187+ ///
188+ /// # Example
189+ ///
190+ /// >>> from pyfaup import Host
191+ /// >>> host = Host("192.168.1.1")
192+ /// >>> print(host.try_into_ip()) # "192.168.1.1"
193+ ///
194+ /// >>> Host("sub.example.com").try_into_ip()
195+ /// Traceback (most recent call last):
196+ /// ...
197+ /// ValueError: host object is not an ip address
91198 pub fn try_into_ip ( & self ) -> PyResult < String > {
92199 match self {
93200 Host :: Hostname ( _) => Err ( PyValueError :: new_err ( "host object is not an ip address" ) ) ,
94201 Host :: Ip ( ip) => Ok ( ip. to_string ( ) ) ,
95202 }
96203 }
97204
205+ /// Returns `True` if the host is a hostname.
206+ ///
207+ /// # Example
208+ ///
209+ /// >>> from pyfaup import Host
210+ /// >>> print(Host("sub.example.com").is_hostname()) # True
211+ /// >>> print(Host("192.168.1.1").is_hostname()) # False
98212 #[ inline( always) ]
99213 pub fn is_hostname ( & self ) -> bool {
100214 matches ! ( self , Host :: Hostname ( _) )
101215 }
102216
217+ /// Returns `True` if the host is an IPv4 address.
218+ ///
219+ /// # Example
220+ ///
221+ /// >>> from pyfaup import Host
222+ /// >>> print(Host("192.168.1.1").is_ipv4()) # True
223+ /// >>> print(Host("::1").is_ipv4()) # False
103224 #[ inline( always) ]
104225 pub fn is_ipv4 ( & self ) -> bool {
105226 matches ! ( self , Host :: Ip ( IpAddr :: V4 ( _) ) )
106227 }
107228
229+ /// Returns `True` if the host is an IPv6 address.
230+ ///
231+ /// # Example
232+ ///
233+ /// >>> from pyfaup import Host
234+ /// >>> print(Host("::1").is_ipv6()) # True
235+ /// >>> print(Host("192.168.1.1").is_ipv6()) # False
108236 #[ inline( always) ]
109237 pub fn is_ipv6 ( & self ) -> bool {
110238 matches ! ( self , Host :: Ip ( IpAddr :: V6 ( _) ) )
111239 }
112240
241+ /// Returns `True` if the host is an IP address (either IPv4 or IPv6).
242+ ///
243+ /// # Example
244+ ///
245+ /// >>> from pyfaup import Host
246+ /// >>> print(Host("192.168.1.1").is_ip_addr()) # True
247+ /// >>> print(Host("sub.example.com").is_ip_addr()) # False
113248 #[ inline( always) ]
114249 pub fn is_ip_addr ( & self ) -> bool {
115250 self . is_ipv4 ( ) | self . is_ipv6 ( )
0 commit comments