1 | use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
2 | |
3 | use crate::conversion::IntoPyObject; |
4 | use crate::exceptions::PyValueError; |
5 | use crate::instance::Bound; |
6 | use crate::sync::GILOnceCell; |
7 | use crate::types::any::PyAnyMethods; |
8 | use crate::types::string::PyStringMethods; |
9 | use crate::types::PyType; |
10 | use crate::{intern, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python}; |
11 | #[allow (deprecated)] |
12 | use crate::{IntoPy, ToPyObject}; |
13 | |
14 | impl FromPyObject<'_> for IpAddr { |
15 | fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> { |
16 | match obj.getattr(attr_name:intern!(obj.py(), "packed" )) { |
17 | Ok(packed: Bound<'_, PyAny>) => { |
18 | if let Ok(packed: [u8; 4]) = packed.extract::<[u8; 4]>() { |
19 | Ok(IpAddr::V4(Ipv4Addr::from(packed))) |
20 | } else if let Ok(packed: [u8; 16]) = packed.extract::<[u8; 16]>() { |
21 | Ok(IpAddr::V6(Ipv6Addr::from(packed))) |
22 | } else { |
23 | Err(PyValueError::new_err(args:"invalid packed length" )) |
24 | } |
25 | } |
26 | Err(_) => { |
27 | // We don't have a .packed attribute, so we try to construct an IP from str(). |
28 | obj.str()?.to_cow()?.parse().map_err(op:PyValueError::new_err) |
29 | } |
30 | } |
31 | } |
32 | } |
33 | |
34 | #[allow (deprecated)] |
35 | impl ToPyObject for Ipv4Addr { |
36 | #[inline ] |
37 | fn to_object(&self, py: Python<'_>) -> PyObject { |
38 | self.into_pyobject(py).unwrap().unbind() |
39 | } |
40 | } |
41 | |
42 | impl<'py> IntoPyObject<'py> for Ipv4Addr { |
43 | type Target = PyAny; |
44 | type Output = Bound<'py, Self::Target>; |
45 | type Error = PyErr; |
46 | |
47 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
48 | static IPV4_ADDRESS: GILOnceCell<Py<PyType>> = GILOnceCell::new(); |
49 | IPV4_ADDRESS |
50 | .import(py, "ipaddress" , "IPv4Address" )? |
51 | .call1((u32::from_be_bytes(self.octets()),)) |
52 | } |
53 | } |
54 | |
55 | impl<'py> IntoPyObject<'py> for &Ipv4Addr { |
56 | type Target = PyAny; |
57 | type Output = Bound<'py, Self::Target>; |
58 | type Error = PyErr; |
59 | |
60 | #[inline ] |
61 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
62 | (*self).into_pyobject(py) |
63 | } |
64 | } |
65 | |
66 | #[allow (deprecated)] |
67 | impl ToPyObject for Ipv6Addr { |
68 | #[inline ] |
69 | fn to_object(&self, py: Python<'_>) -> PyObject { |
70 | self.into_pyobject(py).unwrap().unbind() |
71 | } |
72 | } |
73 | |
74 | impl<'py> IntoPyObject<'py> for Ipv6Addr { |
75 | type Target = PyAny; |
76 | type Output = Bound<'py, Self::Target>; |
77 | type Error = PyErr; |
78 | |
79 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
80 | static IPV6_ADDRESS: GILOnceCell<Py<PyType>> = GILOnceCell::new(); |
81 | IPV6_ADDRESS |
82 | .import(py, "ipaddress" , "IPv6Address" )? |
83 | .call1((u128::from_be_bytes(self.octets()),)) |
84 | } |
85 | } |
86 | |
87 | impl<'py> IntoPyObject<'py> for &Ipv6Addr { |
88 | type Target = PyAny; |
89 | type Output = Bound<'py, Self::Target>; |
90 | type Error = PyErr; |
91 | |
92 | #[inline ] |
93 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
94 | (*self).into_pyobject(py) |
95 | } |
96 | } |
97 | |
98 | #[allow (deprecated)] |
99 | impl ToPyObject for IpAddr { |
100 | #[inline ] |
101 | fn to_object(&self, py: Python<'_>) -> PyObject { |
102 | self.into_pyobject(py).unwrap().unbind() |
103 | } |
104 | } |
105 | |
106 | #[allow (deprecated)] |
107 | impl IntoPy<PyObject> for IpAddr { |
108 | #[inline ] |
109 | fn into_py(self, py: Python<'_>) -> PyObject { |
110 | self.into_pyobject(py).unwrap().unbind() |
111 | } |
112 | } |
113 | |
114 | impl<'py> IntoPyObject<'py> for IpAddr { |
115 | type Target = PyAny; |
116 | type Output = Bound<'py, Self::Target>; |
117 | type Error = PyErr; |
118 | |
119 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
120 | match self { |
121 | IpAddr::V4(ip: Ipv4Addr) => ip.into_pyobject(py), |
122 | IpAddr::V6(ip: Ipv6Addr) => ip.into_pyobject(py), |
123 | } |
124 | } |
125 | } |
126 | |
127 | impl<'py> IntoPyObject<'py> for &IpAddr { |
128 | type Target = PyAny; |
129 | type Output = Bound<'py, Self::Target>; |
130 | type Error = PyErr; |
131 | |
132 | #[inline ] |
133 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
134 | (*self).into_pyobject(py) |
135 | } |
136 | } |
137 | |
138 | #[cfg (test)] |
139 | mod test_ipaddr { |
140 | use std::str::FromStr; |
141 | |
142 | use crate::types::PyString; |
143 | |
144 | use super::*; |
145 | |
146 | #[test ] |
147 | fn test_roundtrip() { |
148 | Python::with_gil(|py| { |
149 | fn roundtrip(py: Python<'_>, ip: &str) { |
150 | let ip = IpAddr::from_str(ip).unwrap(); |
151 | let py_cls = if ip.is_ipv4() { |
152 | "IPv4Address" |
153 | } else { |
154 | "IPv6Address" |
155 | }; |
156 | |
157 | let pyobj = ip.into_pyobject(py).unwrap(); |
158 | let repr = pyobj.repr().unwrap(); |
159 | let repr = repr.to_string_lossy(); |
160 | assert_eq!(repr, format!("{}('{}')" , py_cls, ip)); |
161 | |
162 | let ip2: IpAddr = pyobj.extract().unwrap(); |
163 | assert_eq!(ip, ip2); |
164 | } |
165 | roundtrip(py, "127.0.0.1" ); |
166 | roundtrip(py, "::1" ); |
167 | roundtrip(py, "0.0.0.0" ); |
168 | }); |
169 | } |
170 | |
171 | #[test ] |
172 | fn test_from_pystring() { |
173 | Python::with_gil(|py| { |
174 | let py_str = PyString::new(py, "0:0:0:0:0:0:0:1" ); |
175 | let ip: IpAddr = py_str.extract().unwrap(); |
176 | assert_eq!(ip, IpAddr::from_str("::1" ).unwrap()); |
177 | |
178 | let py_str = PyString::new(py, "invalid" ); |
179 | assert!(py_str.extract::<IpAddr>().is_err()); |
180 | }); |
181 | } |
182 | } |
183 | |