1use std::{cmp, collections, hash};
2
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6 conversion::IntoPyObject,
7 instance::Bound,
8 types::{any::PyAnyMethods, dict::PyDictMethods, PyDict},
9 FromPyObject, PyAny, PyErr, PyObject, Python,
10};
11#[allow(deprecated)]
12use crate::{IntoPy, ToPyObject};
13
14#[allow(deprecated)]
15impl<K, V, H> ToPyObject for collections::HashMap<K, V, H>
16where
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 = PyDict::new(py);
23 for (k, v) in self {
24 dict.set_item(k.to_object(py), v.to_object(py)).unwrap();
25 }
26 dict.into_any().unbind()
27 }
28}
29
30#[allow(deprecated)]
31impl<K, V> ToPyObject for collections::BTreeMap<K, V>
32where
33 K: cmp::Eq + ToPyObject,
34 V: ToPyObject,
35{
36 fn to_object(&self, py: Python<'_>) -> PyObject {
37 let dict = PyDict::new(py);
38 for (k, v) in self {
39 dict.set_item(k.to_object(py), v.to_object(py)).unwrap();
40 }
41 dict.into_any().unbind()
42 }
43}
44
45#[allow(deprecated)]
46impl<K, V, H> IntoPy<PyObject> for collections::HashMap<K, V, H>
47where
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 = PyDict::new(py);
54 for (k, v) in self {
55 dict.set_item(k.into_py(py), v.into_py(py)).unwrap();
56 }
57 dict.into_any().unbind()
58 }
59}
60
61impl<'py, K, V, H> IntoPyObject<'py> for collections::HashMap<K, V, H>
62where
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 = PyDict::new(py);
73 for (k, v) in self {
74 dict.set_item(k, 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
85impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a collections::HashMap<K, V, H>
86where
87 &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
88 &'a V: IntoPyObject<'py>,
89 K: 'a, V: 'a, 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 = PyDict::new(py);
99 for (k, v) in self {
100 dict.set_item(k, 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)]
112impl<K, V> IntoPy<PyObject> for collections::BTreeMap<K, V>
113where
114 K: cmp::Eq + IntoPy<PyObject>,
115 V: IntoPy<PyObject>,
116{
117 fn into_py(self, py: Python<'_>) -> PyObject {
118 let dict = PyDict::new(py);
119 for (k, v) in self {
120 dict.set_item(k.into_py(py), v.into_py(py)).unwrap();
121 }
122 dict.into_any().unbind()
123 }
124}
125
126impl<'py, K, V> IntoPyObject<'py> for collections::BTreeMap<K, V>
127where
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 = PyDict::new(py);
137 for (k, v) in self {
138 dict.set_item(k, 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
149impl<'a, 'py, K, V> IntoPyObject<'py> for &'a collections::BTreeMap<K, V>
150where
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 = PyDict::new(py);
162 for (k, v) in self {
163 dict.set_item(k, 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
174impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap<K, V, S>
175where
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 = ob.downcast::<PyDict>()?;
182 let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default());
183 for (k, v) 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
195impl<'py, K, V> FromPyObject<'py> for collections::BTreeMap<K, V>
196where
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 = ob.downcast::<PyDict>()?;
202 let mut ret = collections::BTreeMap::new();
203 for (k, v) in dict {
204 ret.insert(k.extract()?, 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)]
216mod 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}