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::{try_new_from_iter, PySetMethods},
26 PyDict, PyFrozenSet, PySet,
27 },
28 Bound, FromPyObject, PyAny, PyErr, PyResult, Python,
29};
30use std::{cmp, hash};
31
32impl<'py, K, V, H> IntoPyObject<'py> for hashbrown::HashMap<K, V, H>
33where
34 K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
35 V: IntoPyObject<'py>,
36 H: hash::BuildHasher,
37{
38 type Target = PyDict;
39 type Output = Bound<'py, Self::Target>;
40 type Error = PyErr;
41
42 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
43 let dict = PyDict::new(py);
44 for (k, v) in self {
45 dict.set_item(k, v)?;
46 }
47 Ok(dict)
48 }
49}
50
51impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a hashbrown::HashMap<K, V, H>
52where
53 &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
54 &'a V: IntoPyObject<'py>,
55 H: hash::BuildHasher,
56{
57 type Target = PyDict;
58 type Output = Bound<'py, Self::Target>;
59 type Error = PyErr;
60
61 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
62 let dict = PyDict::new(py);
63 for (k, v) in self {
64 dict.set_item(k, v)?;
65 }
66 Ok(dict)
67 }
68}
69
70impl<'py, K, V, S> FromPyObject<'py> for hashbrown::HashMap<K, V, S>
71where
72 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
73 V: FromPyObject<'py>,
74 S: hash::BuildHasher + Default,
75{
76 fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
77 let dict = ob.downcast::<PyDict>()?;
78 let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default());
79 for (k, v) in dict {
80 ret.insert(k.extract()?, v.extract()?);
81 }
82 Ok(ret)
83 }
84}
85
86impl<'py, K, H> IntoPyObject<'py> for hashbrown::HashSet<K, H>
87where
88 K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
89 H: hash::BuildHasher,
90{
91 type Target = PySet;
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 try_new_from_iter(py, self)
97 }
98}
99
100impl<'a, 'py, K, H> IntoPyObject<'py> for &'a hashbrown::HashSet<K, H>
101where
102 &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
103 H: hash::BuildHasher,
104{
105 type Target = PySet;
106 type Output = Bound<'py, Self::Target>;
107 type Error = PyErr;
108
109 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
110 try_new_from_iter(py, self)
111 }
112}
113
114impl<'py, K, S> FromPyObject<'py> for hashbrown::HashSet<K, S>
115where
116 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
117 S: hash::BuildHasher + Default,
118{
119 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
120 match ob.downcast::<PySet>() {
121 Ok(set) => set.iter().map(|any| any.extract()).collect(),
122 Err(err) => {
123 if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
124 frozen_set.iter().map(|any| any.extract()).collect()
125 } else {
126 Err(PyErr::from(err))
127 }
128 }
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::types::IntoPyDict;
137 use std::collections::hash_map::RandomState;
138
139 #[test]
140 fn test_hashbrown_hashmap_into_pyobject() {
141 Python::with_gil(|py| {
142 let mut map =
143 hashbrown::HashMap::<i32, i32, RandomState>::with_hasher(RandomState::new());
144 map.insert(1, 1);
145
146 let py_map = (&map).into_pyobject(py).unwrap();
147
148 assert!(py_map.len() == 1);
149 assert!(
150 py_map
151 .get_item(1)
152 .unwrap()
153 .unwrap()
154 .extract::<i32>()
155 .unwrap()
156 == 1
157 );
158 assert_eq!(map, py_map.extract().unwrap());
159 });
160 }
161
162 #[test]
163 fn test_hashbrown_hashmap_into_dict() {
164 Python::with_gil(|py| {
165 let mut map =
166 hashbrown::HashMap::<i32, i32, RandomState>::with_hasher(RandomState::new());
167 map.insert(1, 1);
168
169 let py_map = map.into_py_dict(py).unwrap();
170
171 assert_eq!(py_map.len(), 1);
172 assert_eq!(
173 py_map
174 .get_item(1)
175 .unwrap()
176 .unwrap()
177 .extract::<i32>()
178 .unwrap(),
179 1
180 );
181 });
182 }
183
184 #[test]
185 fn test_extract_hashbrown_hashset() {
186 Python::with_gil(|py| {
187 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
188 let hash_set: hashbrown::HashSet<usize, RandomState> = set.extract().unwrap();
189 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
190
191 let set = PyFrozenSet::new(py, [1, 2, 3, 4, 5]).unwrap();
192 let hash_set: hashbrown::HashSet<usize, RandomState> = set.extract().unwrap();
193 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
194 });
195 }
196
197 #[test]
198 fn test_hashbrown_hashset_into_pyobject() {
199 Python::with_gil(|py| {
200 let hs: hashbrown::HashSet<u64, RandomState> =
201 [1, 2, 3, 4, 5].iter().cloned().collect();
202
203 let hso = hs.clone().into_pyobject(py).unwrap();
204
205 assert_eq!(hs, hso.extract().unwrap());
206 });
207 }
208}