1use std::{cmp, collections, hash, ptr};
20
21use crate::conversion::ToPyObject;
22use crate::err::{self, PyErr, PyResult};
23use crate::ffi;
24use crate::objects::{PyList, PyObject};
25use crate::python::{Python, PythonObject};
26
27pub struct PyDict(PyObject);
29
30pyobject_newtype!(PyDict, PyDict_Check, PyDict_Type);
31
32impl PyDict {
33 pub fn new(py: Python) -> PyDict {
37 unsafe { err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_New()) }
38 }
39
40 pub fn copy(&self, py: Python) -> PyResult<PyDict> {
43 unsafe { err::result_cast_from_owned_ptr(py, ffi::PyDict_Copy(self.0.as_ptr())) }
44 }
45
46 #[inline]
48 pub fn clear(&self, _py: Python) {
49 unsafe { ffi::PyDict_Clear(self.0.as_ptr()) }
50 }
51
52 #[inline]
55 pub fn len(&self, _py: Python) -> usize {
56 unsafe { ffi::PyDict_Size(self.0.as_ptr()) as usize }
57 }
58
59 pub fn contains<K>(&self, py: Python, key: K) -> PyResult<bool>
62 where
63 K: ToPyObject,
64 {
65 key.with_borrowed_ptr(py, |key| unsafe {
66 match ffi::PyDict_Contains(self.0.as_ptr(), key) {
67 1 => Ok(true),
68 0 => Ok(false),
69 _ => Err(PyErr::fetch(py)),
70 }
71 })
72 }
73
74 pub fn get_item<K>(&self, py: Python, key: K) -> Option<PyObject>
77 where
78 K: ToPyObject,
79 {
80 key.with_borrowed_ptr(py, |key| unsafe {
81 PyObject::from_borrowed_ptr_opt(py, ffi::PyDict_GetItem(self.0.as_ptr(), key))
82 })
83 }
84
85 pub fn set_item<K, V>(&self, py: Python, key: K, value: V) -> PyResult<()>
88 where
89 K: ToPyObject,
90 V: ToPyObject,
91 {
92 key.with_borrowed_ptr(py, move |key| {
93 value.with_borrowed_ptr(py, |value| unsafe {
94 err::error_on_minusone(py, ffi::PyDict_SetItem(self.0.as_ptr(), key, value))
95 })
96 })
97 }
98
99 pub fn del_item<K>(&self, py: Python, key: K) -> PyResult<()>
102 where
103 K: ToPyObject,
104 {
105 key.with_borrowed_ptr(py, |key| unsafe {
106 err::error_on_minusone(py, ffi::PyDict_DelItem(self.0.as_ptr(), key))
107 })
108 }
109
110 pub fn items_list(&self, py: Python) -> PyList {
113 unsafe { err::cast_from_owned_ptr_or_panic(py, ffi::PyDict_Items(self.0.as_ptr())) }
114 }
115
116 pub fn items(&self, py: Python) -> Vec<(PyObject, PyObject)> {
118 let mut vec = Vec::with_capacity(self.len(py));
122 let mut pos = 0;
123 let mut key: *mut ffi::PyObject = ptr::null_mut();
124 let mut value: *mut ffi::PyObject = ptr::null_mut();
125 unsafe {
126 while ffi::PyDict_Next(self.0.as_ptr(), &mut pos, &mut key, &mut value) != 0 {
127 vec.push((
128 PyObject::from_borrowed_ptr(py, key),
129 PyObject::from_borrowed_ptr(py, value),
130 ));
131 }
132 }
133 vec
134 }
135}
136
137impl<K, V, H> ToPyObject for collections::HashMap<K, V, H>
139where
140 K: hash::Hash + cmp::Eq + ToPyObject,
141 V: ToPyObject,
142 H: hash::BuildHasher,
143{
144 type ObjectType = PyDict;
145
146 fn to_py_object(&self, py: Python) -> PyDict {
147 let dict = PyDict::new(py);
148 for (key, value) in self {
149 dict.set_item(py, key, value).unwrap();
150 }
151 dict
152 }
153}
154
155impl<K, V> ToPyObject for collections::BTreeMap<K, V>
157where
158 K: cmp::Eq + ToPyObject,
159 V: ToPyObject,
160{
161 type ObjectType = PyDict;
162
163 fn to_py_object(&self, py: Python) -> PyDict {
164 let dict = PyDict::new(py);
165 for (key, value) in self {
166 dict.set_item(py, key, value).unwrap();
167 }
168 dict
169 }
170}
171
172#[cfg(test)]
173mod test {
174 use crate::conversion::ToPyObject;
175 use crate::objects::{PyDict, PyTuple};
176 use crate::python::{Python, PythonObject};
177 use std::collections::HashMap;
178
179 #[test]
180 fn test_len() {
181 let gil = Python::acquire_gil();
182 let py = gil.python();
183 let mut v = HashMap::new();
184 let dict = v.to_py_object(py);
185 assert_eq!(0, dict.len(py));
186 v.insert(7, 32);
187 let dict2 = v.to_py_object(py);
188 assert_eq!(1, dict2.len(py));
189 }
190
191 #[test]
192 fn test_contains() {
193 let gil = Python::acquire_gil();
194 let py = gil.python();
195 let mut v = HashMap::new();
196 v.insert(7, 32);
197 let dict = v.to_py_object(py);
198 assert_eq!(true, dict.contains(py, 7i32).unwrap());
199 assert_eq!(false, dict.contains(py, 8i32).unwrap());
200 }
201
202 #[test]
203 fn test_get_item() {
204 let gil = Python::acquire_gil();
205 let py = gil.python();
206 let mut v = HashMap::new();
207 v.insert(7, 32);
208 let dict = v.to_py_object(py);
209 assert_eq!(
210 32,
211 dict.get_item(py, 7i32).unwrap().extract::<i32>(py).unwrap()
212 );
213 assert_eq!(None, dict.get_item(py, 8i32));
214 }
215
216 #[test]
217 fn test_set_item() {
218 let gil = Python::acquire_gil();
219 let py = gil.python();
220 let mut v = HashMap::new();
221 v.insert(7, 32);
222 let dict = v.to_py_object(py);
223 assert!(dict.set_item(py, 7i32, 42i32).is_ok()); assert!(dict.set_item(py, 8i32, 123i32).is_ok()); assert_eq!(
226 42i32,
227 dict.get_item(py, 7i32).unwrap().extract::<i32>(py).unwrap()
228 );
229 assert_eq!(
230 123i32,
231 dict.get_item(py, 8i32).unwrap().extract::<i32>(py).unwrap()
232 );
233 }
234
235 #[test]
236 fn test_set_item_does_not_update_original_object() {
237 let gil = Python::acquire_gil();
238 let py = gil.python();
239 let mut v = HashMap::new();
240 v.insert(7, 32);
241 let dict = v.to_py_object(py);
242 assert!(dict.set_item(py, 7i32, 42i32).is_ok()); assert!(dict.set_item(py, 8i32, 123i32).is_ok()); assert_eq!(32i32, *v.get(&7i32).unwrap()); assert_eq!(None, v.get(&8i32));
246 }
247
248 #[test]
249 fn test_del_item() {
250 let gil = Python::acquire_gil();
251 let py = gil.python();
252 let mut v = HashMap::new();
253 v.insert(7, 32);
254 let dict = v.to_py_object(py);
255 assert!(dict.del_item(py, 7i32).is_ok());
256 assert_eq!(0, dict.len(py));
257 assert_eq!(None, dict.get_item(py, 7i32));
258 }
259
260 #[test]
261 fn test_del_item_does_not_update_original_object() {
262 let gil = Python::acquire_gil();
263 let py = gil.python();
264 let mut v = HashMap::new();
265 v.insert(7, 32);
266 let dict = v.to_py_object(py);
267 assert!(dict.del_item(py, 7i32).is_ok()); assert_eq!(32i32, *v.get(&7i32).unwrap()); }
270
271 #[test]
272 fn test_items_list() {
273 let gil = Python::acquire_gil();
274 let py = gil.python();
275 let mut v = HashMap::new();
276 v.insert(7, 32);
277 v.insert(8, 42);
278 v.insert(9, 123);
279 let dict = v.to_py_object(py);
280 let mut key_sum = 0;
282 let mut value_sum = 0;
283 for el in dict.items_list(py).iter(py) {
284 let tuple = el.cast_into::<PyTuple>(py).unwrap();
285 key_sum += tuple.get_item(py, 0).extract::<i32>(py).unwrap();
286 value_sum += tuple.get_item(py, 1).extract::<i32>(py).unwrap();
287 }
288 assert_eq!(7 + 8 + 9, key_sum);
289 assert_eq!(32 + 42 + 123, value_sum);
290 }
291
292 #[test]
293 fn test_items() {
294 let gil = Python::acquire_gil();
295 let py = gil.python();
296 let mut v = HashMap::new();
297 v.insert(7, 32);
298 v.insert(8, 42);
299 v.insert(9, 123);
300 let dict = v.to_py_object(py);
301 let mut key_sum = 0;
303 let mut value_sum = 0;
304 for (key, value) in dict.items(py) {
305 key_sum += key.extract::<i32>(py).unwrap();
306 value_sum += value.extract::<i32>(py).unwrap();
307 }
308 assert_eq!(7 + 8 + 9, key_sum);
309 assert_eq!(32 + 42 + 123, value_sum);
310 }
311}