1#![cfg(feature = "indexmap")]
2
3#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"indexmap\"] }")]
22use crate::conversion::IntoPyObject;
91use crate::types::*;
92use crate::{Bound, FromPyObject, PyErr, PyObject, Python};
93#[allow(deprecated)]
94use crate::{IntoPy, ToPyObject};
95use std::{cmp, hash};
96
97#[allow(deprecated)]
98impl<K, V, H> ToPyObject for indexmap::IndexMap<K, V, H>
99where
100 K: hash::Hash + cmp::Eq + ToPyObject,
101 V: ToPyObject,
102 H: hash::BuildHasher,
103{
104 fn to_object(&self, py: Python<'_>) -> PyObject {
105 let dict = PyDict::new(py);
106 for (k, v) in self {
107 dict.set_item(k.to_object(py), v.to_object(py)).unwrap();
108 }
109 dict.into_any().unbind()
110 }
111}
112
113#[allow(deprecated)]
114impl<K, V, H> IntoPy<PyObject> for indexmap::IndexMap<K, V, H>
115where
116 K: hash::Hash + cmp::Eq + IntoPy<PyObject>,
117 V: IntoPy<PyObject>,
118 H: hash::BuildHasher,
119{
120 fn into_py(self, py: Python<'_>) -> PyObject {
121 let dict = PyDict::new(py);
122 for (k, v) in self {
123 dict.set_item(k.into_py(py), v.into_py(py)).unwrap();
124 }
125 dict.into_any().unbind()
126 }
127}
128
129impl<'py, K, V, H> IntoPyObject<'py> for indexmap::IndexMap<K, V, H>
130where
131 K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
132 V: IntoPyObject<'py>,
133 H: hash::BuildHasher,
134{
135 type Target = PyDict;
136 type Output = Bound<'py, Self::Target>;
137 type Error = PyErr;
138
139 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
140 let dict = PyDict::new(py);
141 for (k, v) in self {
142 dict.set_item(k, v)?;
143 }
144 Ok(dict)
145 }
146}
147
148impl<'a, 'py, K, V, H> IntoPyObject<'py> for &'a indexmap::IndexMap<K, V, H>
149where
150 &'a K: IntoPyObject<'py> + cmp::Eq + hash::Hash,
151 &'a V: IntoPyObject<'py>,
152 H: hash::BuildHasher,
153{
154 type Target = PyDict;
155 type Output = Bound<'py, Self::Target>;
156 type Error = PyErr;
157
158 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
159 let dict = PyDict::new(py);
160 for (k, v) in self {
161 dict.set_item(k, v)?;
162 }
163 Ok(dict)
164 }
165}
166
167impl<'py, K, V, S> FromPyObject<'py> for indexmap::IndexMap<K, V, S>
168where
169 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
170 V: FromPyObject<'py>,
171 S: hash::BuildHasher + Default,
172{
173 fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
174 let dict = ob.downcast::<PyDict>()?;
175 let mut ret = indexmap::IndexMap::with_capacity_and_hasher(dict.len(), S::default());
176 for (k, v) in dict {
177 ret.insert(k.extract()?, v.extract()?);
178 }
179 Ok(ret)
180 }
181}
182
183#[cfg(test)]
184mod test_indexmap {
185
186 use crate::types::*;
187 use crate::{IntoPyObject, Python};
188
189 #[test]
190 fn test_indexmap_indexmap_into_pyobject() {
191 Python::with_gil(|py| {
192 let mut map = indexmap::IndexMap::<i32, i32>::new();
193 map.insert(1, 1);
194
195 let py_map = (&map).into_pyobject(py).unwrap();
196
197 assert!(py_map.len() == 1);
198 assert!(
199 py_map
200 .get_item(1)
201 .unwrap()
202 .unwrap()
203 .extract::<i32>()
204 .unwrap()
205 == 1
206 );
207 assert_eq!(
208 map,
209 py_map.extract::<indexmap::IndexMap::<i32, i32>>().unwrap()
210 );
211 });
212 }
213
214 #[test]
215 fn test_indexmap_indexmap_into_dict() {
216 Python::with_gil(|py| {
217 let mut map = indexmap::IndexMap::<i32, i32>::new();
218 map.insert(1, 1);
219
220 let py_map = map.into_py_dict(py).unwrap();
221
222 assert_eq!(py_map.len(), 1);
223 assert_eq!(
224 py_map
225 .get_item(1)
226 .unwrap()
227 .unwrap()
228 .extract::<i32>()
229 .unwrap(),
230 1
231 );
232 });
233 }
234
235 #[test]
236 fn test_indexmap_indexmap_insertion_order_round_trip() {
237 Python::with_gil(|py| {
238 let n = 20;
239 let mut map = indexmap::IndexMap::<i32, i32>::new();
240
241 for i in 1..=n {
242 if i % 2 == 1 {
243 map.insert(i, i);
244 } else {
245 map.insert(n - i, i);
246 }
247 }
248
249 let py_map = (&map).into_py_dict(py).unwrap();
250
251 let trip_map = py_map.extract::<indexmap::IndexMap<i32, i32>>().unwrap();
252
253 for (((k1, v1), (k2, v2)), (k3, v3)) in
254 map.iter().zip(py_map.iter()).zip(trip_map.iter())
255 {
256 let k2 = k2.extract::<i32>().unwrap();
257 let v2 = v2.extract::<i32>().unwrap();
258 assert_eq!((k1, v1), (&k2, &v2));
259 assert_eq!((k1, v1), (k3, v3));
260 assert_eq!((&k2, &v2), (k3, v3));
261 }
262 });
263 }
264}