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, Python,
10};
11
12impl<'py, K, V, H> IntoPyObject<'py> for collections::HashMap<K, V, H>
13where
14 K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
15 V: IntoPyObject<'py>,
16 H: hash::BuildHasher,
17{
18 type Target = PyDict;
19 type Output = Bound<'py, Self::Target>;
20 type Error = PyErr;
21
22 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
23 let dict = PyDict::new(py);
24 for (k, v) in self {
25 dict.set_item(k, v)?;
26 }
27 Ok(dict)
28 }
29
30 #[cfg(feature = "experimental-inspect")]
31 fn type_output() -> TypeInfo {
32 TypeInfo::dict_of(K::type_output(), V::type_output())
33 }
34}
35
36impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a collections::HashMap<K, V, H>
37where
38 &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
39 &'a V: IntoPyObject<'py>,
40 K: 'a, V: 'a, H: hash::BuildHasher,
43{
44 type Target = PyDict;
45 type Output = Bound<'py, Self::Target>;
46 type Error = PyErr;
47
48 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
49 let dict = PyDict::new(py);
50 for (k, v) in self {
51 dict.set_item(k, v)?;
52 }
53 Ok(dict)
54 }
55
56 #[cfg(feature = "experimental-inspect")]
57 fn type_output() -> TypeInfo {
58 TypeInfo::dict_of(<&K>::type_output(), <&V>::type_output())
59 }
60}
61
62impl<'py, K, V> IntoPyObject<'py> for collections::BTreeMap<K, V>
63where
64 K: IntoPyObject<'py> + cmp::Eq,
65 V: IntoPyObject<'py>,
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> IntoPyObject<'py> for &'a collections::BTreeMap<K, V>
86where
87 &'a K: IntoPyObject<'py> + cmp::Eq,
88 &'a V: IntoPyObject<'py>,
89 K: 'a,
90 V: 'a,
91{
92 type Target = PyDict;
93 type Output = Bound<'py, Self::Target>;
94 type Error = PyErr;
95
96 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
97 let dict = PyDict::new(py);
98 for (k, v) in self {
99 dict.set_item(k, v)?;
100 }
101 Ok(dict)
102 }
103
104 #[cfg(feature = "experimental-inspect")]
105 fn type_output() -> TypeInfo {
106 TypeInfo::dict_of(<&K>::type_output(), <&V>::type_output())
107 }
108}
109
110impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap<K, V, S>
111where
112 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
113 V: FromPyObject<'py>,
114 S: hash::BuildHasher + Default,
115{
116 fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
117 let dict = ob.downcast::<PyDict>()?;
118 let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default());
119 for (k, v) in dict {
120 ret.insert(k.extract()?, v.extract()?);
121 }
122 Ok(ret)
123 }
124
125 #[cfg(feature = "experimental-inspect")]
126 fn type_input() -> TypeInfo {
127 TypeInfo::mapping_of(K::type_input(), V::type_input())
128 }
129}
130
131impl<'py, K, V> FromPyObject<'py> for collections::BTreeMap<K, V>
132where
133 K: FromPyObject<'py> + cmp::Ord,
134 V: FromPyObject<'py>,
135{
136 fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
137 let dict = ob.downcast::<PyDict>()?;
138 let mut ret = collections::BTreeMap::new();
139 for (k, v) in dict {
140 ret.insert(k.extract()?, v.extract()?);
141 }
142 Ok(ret)
143 }
144
145 #[cfg(feature = "experimental-inspect")]
146 fn type_input() -> TypeInfo {
147 TypeInfo::mapping_of(K::type_input(), V::type_input())
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use std::collections::{BTreeMap, HashMap};
155
156 #[test]
157 fn test_hashmap_to_python() {
158 Python::with_gil(|py| {
159 let mut map = HashMap::<i32, i32>::new();
160 map.insert(1, 1);
161
162 let py_map = (&map).into_pyobject(py).unwrap();
163
164 assert!(py_map.len() == 1);
165 assert!(
166 py_map
167 .get_item(1)
168 .unwrap()
169 .unwrap()
170 .extract::<i32>()
171 .unwrap()
172 == 1
173 );
174 assert_eq!(map, py_map.extract().unwrap());
175 });
176 }
177
178 #[test]
179 fn test_btreemap_to_python() {
180 Python::with_gil(|py| {
181 let mut map = BTreeMap::<i32, i32>::new();
182 map.insert(1, 1);
183
184 let py_map = (&map).into_pyobject(py).unwrap();
185
186 assert!(py_map.len() == 1);
187 assert!(
188 py_map
189 .get_item(1)
190 .unwrap()
191 .unwrap()
192 .extract::<i32>()
193 .unwrap()
194 == 1
195 );
196 assert_eq!(map, py_map.extract().unwrap());
197 });
198 }
199
200 #[test]
201 fn test_hashmap_into_python() {
202 Python::with_gil(|py| {
203 let mut map = HashMap::<i32, i32>::new();
204 map.insert(1, 1);
205
206 let py_map = map.into_pyobject(py).unwrap();
207
208 assert!(py_map.len() == 1);
209 assert!(
210 py_map
211 .get_item(1)
212 .unwrap()
213 .unwrap()
214 .extract::<i32>()
215 .unwrap()
216 == 1
217 );
218 });
219 }
220
221 #[test]
222 fn test_btreemap_into_py() {
223 Python::with_gil(|py| {
224 let mut map = BTreeMap::<i32, i32>::new();
225 map.insert(1, 1);
226
227 let py_map = map.into_pyobject(py).unwrap();
228
229 assert!(py_map.len() == 1);
230 assert!(
231 py_map
232 .get_item(1)
233 .unwrap()
234 .unwrap()
235 .extract::<i32>()
236 .unwrap()
237 == 1
238 );
239 });
240 }
241}