1use std::{cmp, collections, hash};
2
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6 types::{IntoPyDict, PyDict},
7 FromPyObject, IntoPy, PyAny, PyErr, PyObject, Python, ToPyObject,
8};
9
10impl<K, V, H> ToPyObject for collections::HashMap<K, V, H>
11where
12 K: hash::Hash + cmp::Eq + ToPyObject,
13 V: ToPyObject,
14 H: hash::BuildHasher,
15{
16 fn to_object(&self, py: Python<'_>) -> PyObject {
17 IntoPyDict::into_py_dict(self, py).into()
18 }
19}
20
21impl<K, V> ToPyObject for collections::BTreeMap<K, V>
22where
23 K: cmp::Eq + ToPyObject,
24 V: ToPyObject,
25{
26 fn to_object(&self, py: Python<'_>) -> PyObject {
27 IntoPyDict::into_py_dict(self, py).into()
28 }
29}
30
31impl<K, V, H> IntoPy<PyObject> for collections::HashMap<K, V, H>
32where
33 K: hash::Hash + cmp::Eq + IntoPy<PyObject>,
34 V: IntoPy<PyObject>,
35 H: hash::BuildHasher,
36{
37 fn into_py(self, py: Python<'_>) -> PyObject {
38 let iter: impl Iterator, …)> = self
39 .into_iter()
40 .map(|(k: K, v: V)| (k.into_py(py), v.into_py(py)));
41 IntoPyDict::into_py_dict(self:iter, py).into()
42 }
43
44 #[cfg(feature = "experimental-inspect")]
45 fn type_output() -> TypeInfo {
46 TypeInfo::dict_of(K::type_output(), V::type_output())
47 }
48}
49
50impl<K, V> IntoPy<PyObject> for collections::BTreeMap<K, V>
51where
52 K: cmp::Eq + IntoPy<PyObject>,
53 V: IntoPy<PyObject>,
54{
55 fn into_py(self, py: Python<'_>) -> PyObject {
56 let iter: impl Iterator, …)> = self
57 .into_iter()
58 .map(|(k: K, v: V)| (k.into_py(py), v.into_py(py)));
59 IntoPyDict::into_py_dict(self:iter, py).into()
60 }
61
62 #[cfg(feature = "experimental-inspect")]
63 fn type_output() -> TypeInfo {
64 TypeInfo::dict_of(K::type_output(), V::type_output())
65 }
66}
67
68impl<'source, K, V, S> FromPyObject<'source> for collections::HashMap<K, V, S>
69where
70 K: FromPyObject<'source> + cmp::Eq + hash::Hash,
71 V: FromPyObject<'source>,
72 S: hash::BuildHasher + Default,
73{
74 fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
75 let dict: &PyDict = ob.downcast()?;
76 let mut ret: HashMap = collections::HashMap::with_capacity_and_hasher(capacity:dict.len(), S::default());
77 for (k: &PyAny, v: &PyAny) in dict {
78 ret.insert(K::extract(k)?, V::extract(ob:v)?);
79 }
80 Ok(ret)
81 }
82
83 #[cfg(feature = "experimental-inspect")]
84 fn type_input() -> TypeInfo {
85 TypeInfo::mapping_of(K::type_input(), V::type_input())
86 }
87}
88
89impl<'source, K, V> FromPyObject<'source> for collections::BTreeMap<K, V>
90where
91 K: FromPyObject<'source> + cmp::Ord,
92 V: FromPyObject<'source>,
93{
94 fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
95 let dict: &PyDict = ob.downcast()?;
96 let mut ret: BTreeMap = collections::BTreeMap::new();
97 for (k: &PyAny, v: &PyAny) in dict {
98 ret.insert(K::extract(k)?, V::extract(ob:v)?);
99 }
100 Ok(ret)
101 }
102
103 #[cfg(feature = "experimental-inspect")]
104 fn type_input() -> TypeInfo {
105 TypeInfo::mapping_of(K::type_input(), V::type_input())
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use crate::{IntoPy, PyObject, Python, ToPyObject};
113 use std::collections::{BTreeMap, HashMap};
114
115 #[test]
116 fn test_hashmap_to_python() {
117 Python::with_gil(|py| {
118 let mut map = HashMap::<i32, i32>::new();
119 map.insert(1, 1);
120
121 let m = map.to_object(py);
122 let py_map: &PyDict = m.downcast(py).unwrap();
123
124 assert!(py_map.len() == 1);
125 assert!(
126 py_map
127 .get_item(1)
128 .unwrap()
129 .unwrap()
130 .extract::<i32>()
131 .unwrap()
132 == 1
133 );
134 assert_eq!(map, py_map.extract().unwrap());
135 });
136 }
137
138 #[test]
139 fn test_btreemap_to_python() {
140 Python::with_gil(|py| {
141 let mut map = BTreeMap::<i32, i32>::new();
142 map.insert(1, 1);
143
144 let m = map.to_object(py);
145 let py_map: &PyDict = m.downcast(py).unwrap();
146
147 assert!(py_map.len() == 1);
148 assert!(
149 py_map
150 .get_item(1)
151 .unwrap()
152 .unwrap()
153 .extract::<i32>()
154 .unwrap()
155 == 1
156 );
157 assert_eq!(map, py_map.extract().unwrap());
158 });
159 }
160
161 #[test]
162 fn test_hashmap_into_python() {
163 Python::with_gil(|py| {
164 let mut map = HashMap::<i32, i32>::new();
165 map.insert(1, 1);
166
167 let m: PyObject = map.into_py(py);
168 let py_map: &PyDict = m.downcast(py).unwrap();
169
170 assert!(py_map.len() == 1);
171 assert!(
172 py_map
173 .get_item(1)
174 .unwrap()
175 .unwrap()
176 .extract::<i32>()
177 .unwrap()
178 == 1
179 );
180 });
181 }
182
183 #[test]
184 fn test_btreemap_into_py() {
185 Python::with_gil(|py| {
186 let mut map = BTreeMap::<i32, i32>::new();
187 map.insert(1, 1);
188
189 let m: PyObject = map.into_py(py);
190 let py_map: &PyDict = m.downcast(py).unwrap();
191
192 assert!(py_map.len() == 1);
193 assert!(
194 py_map
195 .get_item(1)
196 .unwrap()
197 .unwrap()
198 .extract::<i32>()
199 .unwrap()
200 == 1
201 );
202 });
203 }
204}
205