1#![cfg(feature = "hashbrown")]
2
3#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"hashbrown\"] }")]
15use crate::{
20 conversion::IntoPyObject,
21 types::{
22 any::PyAnyMethods,
23 dict::PyDictMethods,
24 frozenset::PyFrozenSetMethods,
25 set::{new_from_iter, try_new_from_iter, PySetMethods},
26 PyDict, PyFrozenSet, PySet,
27 },
28 Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python,
29};
30#[allow(deprecated)]
31use crate::{IntoPy, ToPyObject};
32use std::{cmp, hash};
33
34#[allow(deprecated)]
35impl<K, V, H> ToPyObject for hashbrown::HashMap<K, V, H>
36where
37 K: hash::Hash + cmp::Eq + ToPyObject,
38 V: ToPyObject,
39 H: hash::BuildHasher,
40{
41 fn to_object(&self, py: Python<'_>) -> PyObject {
42 let dict = PyDict::new(py);
43 for (k, v) in self {
44 dict.set_item(k.to_object(py), v.to_object(py)).unwrap();
45 }
46 dict.into_any().unbind()
47 }
48}
49
50#[allow(deprecated)]
51impl<K, V, H> IntoPy<PyObject> for hashbrown::HashMap<K, V, H>
52where
53 K: hash::Hash + cmp::Eq + IntoPy<PyObject>,
54 V: IntoPy<PyObject>,
55 H: hash::BuildHasher,
56{
57 fn into_py(self, py: Python<'_>) -> PyObject {
58 let dict = PyDict::new(py);
59 for (k, v) in self {
60 dict.set_item(k.into_py(py), v.into_py(py)).unwrap();
61 }
62 dict.into_any().unbind()
63 }
64}
65
66impl<'py, K, V, H> IntoPyObject<'py> for hashbrown::HashMap<K, V, H>
67where
68 K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
69 V: IntoPyObject<'py>,
70 H: hash::BuildHasher,
71{
72 type Target = PyDict;
73 type Output = Bound<'py, Self::Target>;
74 type Error = PyErr;
75
76 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
77 let dict = PyDict::new(py);
78 for (k, v) in self {
79 dict.set_item(k, v)?;
80 }
81 Ok(dict)
82 }
83}
84
85impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a hashbrown::HashMap<K, V, H>
86where
87 &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
88 &'a V: IntoPyObject<'py>,
89 H: hash::BuildHasher,
90{
91 type Target = PyDict;
92 type Output = Bound<'py, Self::Target>;
93 type Error = PyErr;
94
95 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
96 let dict = PyDict::new(py);
97 for (k, v) in self {
98 dict.set_item(k, v)?;
99 }
100 Ok(dict)
101 }
102}
103
104impl<'py, K, V, S> FromPyObject<'py> for hashbrown::HashMap<K, V, S>
105where
106 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
107 V: FromPyObject<'py>,
108 S: hash::BuildHasher + Default,
109{
110 fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
111 let dict = ob.downcast::<PyDict>()?;
112 let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default());
113 for (k, v) in dict {
114 ret.insert(k.extract()?, v.extract()?);
115 }
116 Ok(ret)
117 }
118}
119
120#[allow(deprecated)]
121impl<T> ToPyObject for hashbrown::HashSet<T>
122where
123 T: hash::Hash + Eq + ToPyObject,
124{
125 fn to_object(&self, py: Python<'_>) -> PyObject {
126 new_from_iter(py, self)
127 .expect("Failed to create Python set from hashbrown::HashSet")
128 .into()
129 }
130}
131
132#[allow(deprecated)]
133impl<K, S> IntoPy<PyObject> for hashbrown::HashSet<K, S>
134where
135 K: IntoPy<PyObject> + Eq + hash::Hash,
136 S: hash::BuildHasher + Default,
137{
138 fn into_py(self, py: Python<'_>) -> PyObject {
139 new_from_iter(py, self.into_iter().map(|item| item.into_py(py)))
140 .expect("Failed to create Python set from hashbrown::HashSet")
141 .into()
142 }
143}
144
145impl<'py, K, H> IntoPyObject<'py> for hashbrown::HashSet<K, H>
146where
147 K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
148 H: hash::BuildHasher,
149{
150 type Target = PySet;
151 type Output = Bound<'py, Self::Target>;
152 type Error = PyErr;
153
154 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
155 try_new_from_iter(py, self)
156 }
157}
158
159impl<'a, 'py, K, H> IntoPyObject<'py> for &'a hashbrown::HashSet<K, H>
160where
161 &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
162 H: hash::BuildHasher,
163{
164 type Target = PySet;
165 type Output = Bound<'py, Self::Target>;
166 type Error = PyErr;
167
168 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
169 try_new_from_iter(py, self)
170 }
171}
172
173impl<'py, K, S> FromPyObject<'py> for hashbrown::HashSet<K, S>
174where
175 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
176 S: hash::BuildHasher + Default,
177{
178 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
179 match ob.downcast::<PySet>() {
180 Ok(set) => set.iter().map(|any| any.extract()).collect(),
181 Err(err) => {
182 if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
183 frozen_set.iter().map(|any| any.extract()).collect()
184 } else {
185 Err(PyErr::from(err))
186 }
187 }
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use crate::types::IntoPyDict;
196
197 #[test]
198 fn test_hashbrown_hashmap_into_pyobject() {
199 Python::with_gil(|py| {
200 let mut map = hashbrown::HashMap::<i32, i32>::new();
201 map.insert(1, 1);
202
203 let py_map = (&map).into_pyobject(py).unwrap();
204
205 assert!(py_map.len() == 1);
206 assert!(
207 py_map
208 .get_item(1)
209 .unwrap()
210 .unwrap()
211 .extract::<i32>()
212 .unwrap()
213 == 1
214 );
215 assert_eq!(map, py_map.extract().unwrap());
216 });
217 }
218
219 #[test]
220 fn test_hashbrown_hashmap_into_dict() {
221 Python::with_gil(|py| {
222 let mut map = hashbrown::HashMap::<i32, i32>::new();
223 map.insert(1, 1);
224
225 let py_map = map.into_py_dict(py).unwrap();
226
227 assert_eq!(py_map.len(), 1);
228 assert_eq!(
229 py_map
230 .get_item(1)
231 .unwrap()
232 .unwrap()
233 .extract::<i32>()
234 .unwrap(),
235 1
236 );
237 });
238 }
239
240 #[test]
241 fn test_extract_hashbrown_hashset() {
242 Python::with_gil(|py| {
243 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
244 let hash_set: hashbrown::HashSet<usize> = set.extract().unwrap();
245 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
246
247 let set = PyFrozenSet::new(py, [1, 2, 3, 4, 5]).unwrap();
248 let hash_set: hashbrown::HashSet<usize> = set.extract().unwrap();
249 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
250 });
251 }
252
253 #[test]
254 fn test_hashbrown_hashset_into_pyobject() {
255 Python::with_gil(|py| {
256 let hs: hashbrown::HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
257
258 let hso = hs.clone().into_pyobject(py).unwrap();
259
260 assert_eq!(hs, hso.extract().unwrap());
261 });
262 }
263}