1 | use std::{cmp, collections, hash}; |
2 | |
3 | #[cfg (feature = "experimental-inspect" )] |
4 | use crate::inspect::types::TypeInfo; |
5 | use crate::{ |
6 | conversion::IntoPyObject, |
7 | instance::Bound, |
8 | types::{any::PyAnyMethods, dict::PyDictMethods, PyDict}, |
9 | FromPyObject, PyAny, PyErr, PyObject, Python, |
10 | }; |
11 | #[allow (deprecated)] |
12 | use crate::{IntoPy, ToPyObject}; |
13 | |
14 | #[allow (deprecated)] |
15 | impl<K, V, H> ToPyObject for collections::HashMap<K, V, H> |
16 | where |
17 | K: hash::Hash + cmp::Eq + ToPyObject, |
18 | V: ToPyObject, |
19 | H: hash::BuildHasher, |
20 | { |
21 | fn to_object(&self, py: Python<'_>) -> PyObject { |
22 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
23 | for (k: &K, v: &V) in self { |
24 | dict.set_item(key:k.to_object(py), value:v.to_object(py)).unwrap(); |
25 | } |
26 | dict.into_any().unbind() |
27 | } |
28 | } |
29 | |
30 | #[allow (deprecated)] |
31 | impl<K, V> ToPyObject for collections::BTreeMap<K, V> |
32 | where |
33 | K: cmp::Eq + ToPyObject, |
34 | V: ToPyObject, |
35 | { |
36 | fn to_object(&self, py: Python<'_>) -> PyObject { |
37 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
38 | for (k: &K, v: &V) in self { |
39 | dict.set_item(key:k.to_object(py), value:v.to_object(py)).unwrap(); |
40 | } |
41 | dict.into_any().unbind() |
42 | } |
43 | } |
44 | |
45 | #[allow (deprecated)] |
46 | impl<K, V, H> IntoPy<PyObject> for collections::HashMap<K, V, H> |
47 | where |
48 | K: hash::Hash + cmp::Eq + IntoPy<PyObject>, |
49 | V: IntoPy<PyObject>, |
50 | H: hash::BuildHasher, |
51 | { |
52 | fn into_py(self, py: Python<'_>) -> PyObject { |
53 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
54 | for (k: K, v: V) in self { |
55 | dict.set_item(key:k.into_py(py), value:v.into_py(py)).unwrap(); |
56 | } |
57 | dict.into_any().unbind() |
58 | } |
59 | } |
60 | |
61 | impl<'py, K, V, H> IntoPyObject<'py> for collections::HashMap<K, V, H> |
62 | where |
63 | K: IntoPyObject<'py> + cmp::Eq + hash::Hash, |
64 | V: IntoPyObject<'py>, |
65 | H: hash::BuildHasher, |
66 | { |
67 | type Target = PyDict; |
68 | type Output = Bound<'py, Self::Target>; |
69 | type Error = PyErr; |
70 | |
71 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
72 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
73 | for (k: K, v: V) in self { |
74 | dict.set_item(key:k, value:v)?; |
75 | } |
76 | Ok(dict) |
77 | } |
78 | |
79 | #[cfg (feature = "experimental-inspect" )] |
80 | fn type_output() -> TypeInfo { |
81 | TypeInfo::dict_of(K::type_output(), V::type_output()) |
82 | } |
83 | } |
84 | |
85 | impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a collections::HashMap<K, V, H> |
86 | where |
87 | &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash, |
88 | &'a V: IntoPyObject<'py>, |
89 | K: 'a, // MSRV |
90 | V: 'a, // MSRV |
91 | H: hash::BuildHasher, |
92 | { |
93 | type Target = PyDict; |
94 | type Output = Bound<'py, Self::Target>; |
95 | type Error = PyErr; |
96 | |
97 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
98 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
99 | for (k: &'a K, v: &'a V) in self { |
100 | dict.set_item(key:k, value:v)?; |
101 | } |
102 | Ok(dict) |
103 | } |
104 | |
105 | #[cfg (feature = "experimental-inspect" )] |
106 | fn type_output() -> TypeInfo { |
107 | TypeInfo::dict_of(<&K>::type_output(), <&V>::type_output()) |
108 | } |
109 | } |
110 | |
111 | #[allow (deprecated)] |
112 | impl<K, V> IntoPy<PyObject> for collections::BTreeMap<K, V> |
113 | where |
114 | K: cmp::Eq + IntoPy<PyObject>, |
115 | V: IntoPy<PyObject>, |
116 | { |
117 | fn into_py(self, py: Python<'_>) -> PyObject { |
118 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
119 | for (k: K, v: V) in self { |
120 | dict.set_item(key:k.into_py(py), value:v.into_py(py)).unwrap(); |
121 | } |
122 | dict.into_any().unbind() |
123 | } |
124 | } |
125 | |
126 | impl<'py, K, V> IntoPyObject<'py> for collections::BTreeMap<K, V> |
127 | where |
128 | K: IntoPyObject<'py> + cmp::Eq, |
129 | V: IntoPyObject<'py>, |
130 | { |
131 | type Target = PyDict; |
132 | type Output = Bound<'py, Self::Target>; |
133 | type Error = PyErr; |
134 | |
135 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
136 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
137 | for (k: K, v: V) in self { |
138 | dict.set_item(key:k, value:v)?; |
139 | } |
140 | Ok(dict) |
141 | } |
142 | |
143 | #[cfg (feature = "experimental-inspect" )] |
144 | fn type_output() -> TypeInfo { |
145 | TypeInfo::dict_of(K::type_output(), V::type_output()) |
146 | } |
147 | } |
148 | |
149 | impl<'a, 'py, K, V> IntoPyObject<'py> for &'a collections::BTreeMap<K, V> |
150 | where |
151 | &'a K: IntoPyObject<'py> + cmp::Eq, |
152 | &'a V: IntoPyObject<'py>, |
153 | K: 'a, |
154 | V: 'a, |
155 | { |
156 | type Target = PyDict; |
157 | type Output = Bound<'py, Self::Target>; |
158 | type Error = PyErr; |
159 | |
160 | fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { |
161 | let dict: Bound<'_, PyDict> = PyDict::new(py); |
162 | for (k: &'a K, v: &'a V) in self { |
163 | dict.set_item(key:k, value:v)?; |
164 | } |
165 | Ok(dict) |
166 | } |
167 | |
168 | #[cfg (feature = "experimental-inspect" )] |
169 | fn type_output() -> TypeInfo { |
170 | TypeInfo::dict_of(<&K>::type_output(), <&V>::type_output()) |
171 | } |
172 | } |
173 | |
174 | impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap<K, V, S> |
175 | where |
176 | K: FromPyObject<'py> + cmp::Eq + hash::Hash, |
177 | V: FromPyObject<'py>, |
178 | S: hash::BuildHasher + Default, |
179 | { |
180 | fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> { |
181 | let dict: &Bound<'_, PyDict> = ob.downcast::<PyDict>()?; |
182 | let mut ret: HashMap = collections::HashMap::with_capacity_and_hasher(capacity:dict.len(), S::default()); |
183 | for (k: Bound<'py, PyAny>, v: Bound<'py, PyAny>) in dict { |
184 | ret.insert(k.extract()?, v.extract()?); |
185 | } |
186 | Ok(ret) |
187 | } |
188 | |
189 | #[cfg (feature = "experimental-inspect" )] |
190 | fn type_input() -> TypeInfo { |
191 | TypeInfo::mapping_of(K::type_input(), V::type_input()) |
192 | } |
193 | } |
194 | |
195 | impl<'py, K, V> FromPyObject<'py> for collections::BTreeMap<K, V> |
196 | where |
197 | K: FromPyObject<'py> + cmp::Ord, |
198 | V: FromPyObject<'py>, |
199 | { |
200 | fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> { |
201 | let dict: &Bound<'_, PyDict> = ob.downcast::<PyDict>()?; |
202 | let mut ret: BTreeMap = collections::BTreeMap::new(); |
203 | for (k: Bound<'py, PyAny>, v: Bound<'py, PyAny>) in dict { |
204 | ret.insert(key:k.extract()?, value:v.extract()?); |
205 | } |
206 | Ok(ret) |
207 | } |
208 | |
209 | #[cfg (feature = "experimental-inspect" )] |
210 | fn type_input() -> TypeInfo { |
211 | TypeInfo::mapping_of(K::type_input(), V::type_input()) |
212 | } |
213 | } |
214 | |
215 | #[cfg (test)] |
216 | mod tests { |
217 | use super::*; |
218 | use std::collections::{BTreeMap, HashMap}; |
219 | |
220 | #[test ] |
221 | fn test_hashmap_to_python() { |
222 | Python::with_gil(|py| { |
223 | let mut map = HashMap::<i32, i32>::new(); |
224 | map.insert(1, 1); |
225 | |
226 | let py_map = (&map).into_pyobject(py).unwrap(); |
227 | |
228 | assert!(py_map.len() == 1); |
229 | assert!( |
230 | py_map |
231 | .get_item(1) |
232 | .unwrap() |
233 | .unwrap() |
234 | .extract::<i32>() |
235 | .unwrap() |
236 | == 1 |
237 | ); |
238 | assert_eq!(map, py_map.extract().unwrap()); |
239 | }); |
240 | } |
241 | |
242 | #[test ] |
243 | fn test_btreemap_to_python() { |
244 | Python::with_gil(|py| { |
245 | let mut map = BTreeMap::<i32, i32>::new(); |
246 | map.insert(1, 1); |
247 | |
248 | let py_map = (&map).into_pyobject(py).unwrap(); |
249 | |
250 | assert!(py_map.len() == 1); |
251 | assert!( |
252 | py_map |
253 | .get_item(1) |
254 | .unwrap() |
255 | .unwrap() |
256 | .extract::<i32>() |
257 | .unwrap() |
258 | == 1 |
259 | ); |
260 | assert_eq!(map, py_map.extract().unwrap()); |
261 | }); |
262 | } |
263 | |
264 | #[test ] |
265 | fn test_hashmap_into_python() { |
266 | Python::with_gil(|py| { |
267 | let mut map = HashMap::<i32, i32>::new(); |
268 | map.insert(1, 1); |
269 | |
270 | let py_map = map.into_pyobject(py).unwrap(); |
271 | |
272 | assert!(py_map.len() == 1); |
273 | assert!( |
274 | py_map |
275 | .get_item(1) |
276 | .unwrap() |
277 | .unwrap() |
278 | .extract::<i32>() |
279 | .unwrap() |
280 | == 1 |
281 | ); |
282 | }); |
283 | } |
284 | |
285 | #[test ] |
286 | fn test_btreemap_into_py() { |
287 | Python::with_gil(|py| { |
288 | let mut map = BTreeMap::<i32, i32>::new(); |
289 | map.insert(1, 1); |
290 | |
291 | let py_map = map.into_pyobject(py).unwrap(); |
292 | |
293 | assert!(py_map.len() == 1); |
294 | assert!( |
295 | py_map |
296 | .get_item(1) |
297 | .unwrap() |
298 | .unwrap() |
299 | .extract::<i32>() |
300 | .unwrap() |
301 | == 1 |
302 | ); |
303 | }); |
304 | } |
305 | } |
306 | |