1use crate::types::PyIterator;
2use crate::{
3 err::{self, PyErr, PyResult},
4 ffi,
5 ffi_ptr_ext::FfiPtrExt,
6 py_result_ext::PyResultExt,
7 types::any::PyAnyMethods,
8 Bound, PyAny, Python,
9};
10use crate::{Borrowed, BoundObject, IntoPyObject, IntoPyObjectExt};
11use std::ptr;
12
13pub struct PyFrozenSetBuilder<'py> {
15 py_frozen_set: Bound<'py, PyFrozenSet>,
16}
17
18impl<'py> PyFrozenSetBuilder<'py> {
19 pub fn new(py: Python<'py>) -> PyResult<PyFrozenSetBuilder<'py>> {
23 Ok(PyFrozenSetBuilder {
24 py_frozen_set: PyFrozenSet::empty(py)?,
25 })
26 }
27
28 pub fn add<K>(&mut self, key: K) -> PyResult<()>
30 where
31 K: IntoPyObject<'py>,
32 {
33 fn inner(frozenset: &Bound<'_, PyFrozenSet>, key: Borrowed<'_, '_, PyAny>) -> PyResult<()> {
34 err::error_on_minusone(frozenset.py(), unsafe {
35 ffi::PySet_Add(frozenset.as_ptr(), key.as_ptr())
36 })
37 }
38
39 inner(
40 &self.py_frozen_set,
41 key.into_pyobject(self.py_frozen_set.py())
42 .map_err(Into::into)?
43 .into_any()
44 .as_borrowed(),
45 )
46 }
47
48 pub fn finalize(self) -> Bound<'py, PyFrozenSet> {
50 self.py_frozen_set
51 }
52
53 #[deprecated(since = "0.23.0", note = "renamed to `PyFrozenSetBuilder::finalize`")]
55 #[inline]
56 pub fn finalize_bound(self) -> Bound<'py, PyFrozenSet> {
57 self.finalize()
58 }
59}
60
61#[repr(transparent)]
69pub struct PyFrozenSet(PyAny);
70
71#[cfg(not(any(PyPy, GraalPy)))]
72pyobject_subclassable_native_type!(PyFrozenSet, crate::ffi::PySetObject);
73#[cfg(not(any(PyPy, GraalPy)))]
74pyobject_native_type!(
75 PyFrozenSet,
76 ffi::PySetObject,
77 pyobject_native_static_type_object!(ffi::PyFrozenSet_Type),
78 #checkfunction=ffi::PyFrozenSet_Check
79);
80
81#[cfg(any(PyPy, GraalPy))]
82pyobject_native_type_core!(
83 PyFrozenSet,
84 pyobject_native_static_type_object!(ffi::PyFrozenSet_Type),
85 #checkfunction=ffi::PyFrozenSet_Check
86);
87
88impl PyFrozenSet {
89 #[inline]
93 pub fn new<'py, T>(
94 py: Python<'py>,
95 elements: impl IntoIterator<Item = T>,
96 ) -> PyResult<Bound<'py, PyFrozenSet>>
97 where
98 T: IntoPyObject<'py>,
99 {
100 try_new_from_iter(py, elements)
101 }
102
103 #[deprecated(since = "0.23.0", note = "renamed to `PyFrozenSet::new`")]
105 #[allow(deprecated)]
106 #[inline]
107 pub fn new_bound<'a, 'p, T: crate::ToPyObject + 'a>(
108 py: Python<'p>,
109 elements: impl IntoIterator<Item = &'a T>,
110 ) -> PyResult<Bound<'p, PyFrozenSet>> {
111 Self::new(py, elements.into_iter().map(|e| e.to_object(py)))
112 }
113
114 pub fn empty(py: Python<'_>) -> PyResult<Bound<'_, PyFrozenSet>> {
116 unsafe {
117 ffi::PyFrozenSet_New(ptr::null_mut())
118 .assume_owned_or_err(py)
119 .downcast_into_unchecked()
120 }
121 }
122
123 #[deprecated(since = "0.23.0", note = "renamed to `PyFrozenSet::empty`")]
125 #[inline]
126 pub fn empty_bound(py: Python<'_>) -> PyResult<Bound<'_, PyFrozenSet>> {
127 Self::empty(py)
128 }
129}
130
131#[doc(alias = "PyFrozenSet")]
137pub trait PyFrozenSetMethods<'py>: crate::sealed::Sealed {
138 fn len(&self) -> usize;
142
143 fn is_empty(&self) -> bool {
145 self.len() == 0
146 }
147
148 fn contains<K>(&self, key: K) -> PyResult<bool>
152 where
153 K: IntoPyObject<'py>;
154
155 fn iter(&self) -> BoundFrozenSetIterator<'py>;
157}
158
159impl<'py> PyFrozenSetMethods<'py> for Bound<'py, PyFrozenSet> {
160 #[inline]
161 fn len(&self) -> usize {
162 unsafe { ffi::PySet_Size(self.as_ptr()) as usize }
163 }
164
165 fn contains<K>(&self, key: K) -> PyResult<bool>
166 where
167 K: IntoPyObject<'py>,
168 {
169 fn inner(
170 frozenset: &Bound<'_, PyFrozenSet>,
171 key: Borrowed<'_, '_, PyAny>,
172 ) -> PyResult<bool> {
173 match unsafe { ffi::PySet_Contains(frozenset.as_ptr(), key.as_ptr()) } {
174 1 => Ok(true),
175 0 => Ok(false),
176 _ => Err(PyErr::fetch(frozenset.py())),
177 }
178 }
179
180 let py = self.py();
181 inner(
182 self,
183 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
184 )
185 }
186
187 fn iter(&self) -> BoundFrozenSetIterator<'py> {
188 BoundFrozenSetIterator::new(self.clone())
189 }
190}
191
192impl<'py> IntoIterator for Bound<'py, PyFrozenSet> {
193 type Item = Bound<'py, PyAny>;
194 type IntoIter = BoundFrozenSetIterator<'py>;
195
196 fn into_iter(self) -> Self::IntoIter {
198 BoundFrozenSetIterator::new(self)
199 }
200}
201
202impl<'py> IntoIterator for &Bound<'py, PyFrozenSet> {
203 type Item = Bound<'py, PyAny>;
204 type IntoIter = BoundFrozenSetIterator<'py>;
205
206 fn into_iter(self) -> Self::IntoIter {
208 self.iter()
209 }
210}
211
212pub struct BoundFrozenSetIterator<'p> {
214 it: Bound<'p, PyIterator>,
215 remaining: usize,
217}
218
219impl<'py> BoundFrozenSetIterator<'py> {
220 pub(super) fn new(set: Bound<'py, PyFrozenSet>) -> Self {
221 Self {
222 it: PyIterator::from_object(&set).unwrap(),
223 remaining: set.len(),
224 }
225 }
226}
227
228impl<'py> Iterator for BoundFrozenSetIterator<'py> {
229 type Item = Bound<'py, super::PyAny>;
230
231 fn next(&mut self) -> Option<Self::Item> {
233 self.remaining = self.remaining.saturating_sub(1);
234 self.it.next().map(Result::unwrap)
235 }
236
237 fn size_hint(&self) -> (usize, Option<usize>) {
238 (self.remaining, Some(self.remaining))
239 }
240}
241
242impl ExactSizeIterator for BoundFrozenSetIterator<'_> {
243 fn len(&self) -> usize {
244 self.remaining
245 }
246}
247
248#[inline]
249pub(crate) fn try_new_from_iter<'py, T>(
250 py: Python<'py>,
251 elements: impl IntoIterator<Item = T>,
252) -> PyResult<Bound<'py, PyFrozenSet>>
253where
254 T: IntoPyObject<'py>,
255{
256 let set = unsafe {
257 ffi::PyFrozenSet_New(std::ptr::null_mut())
259 .assume_owned_or_err(py)?
260 .downcast_into_unchecked()
261 };
262 let ptr = set.as_ptr();
263
264 for e in elements {
265 let obj = e.into_pyobject_or_pyerr(py)?;
266 err::error_on_minusone(py, unsafe { ffi::PySet_Add(ptr, obj.as_ptr()) })?;
267 }
268
269 Ok(set)
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn test_frozenset_new_and_len() {
278 Python::with_gil(|py| {
279 let set = PyFrozenSet::new(py, [1]).unwrap();
280 assert_eq!(1, set.len());
281
282 let v = vec![1];
283 assert!(PyFrozenSet::new(py, &[v]).is_err());
284 });
285 }
286
287 #[test]
288 fn test_frozenset_empty() {
289 Python::with_gil(|py| {
290 let set = PyFrozenSet::empty(py).unwrap();
291 assert_eq!(0, set.len());
292 assert!(set.is_empty());
293 });
294 }
295
296 #[test]
297 fn test_frozenset_contains() {
298 Python::with_gil(|py| {
299 let set = PyFrozenSet::new(py, [1]).unwrap();
300 assert!(set.contains(1).unwrap());
301 });
302 }
303
304 #[test]
305 fn test_frozenset_iter() {
306 Python::with_gil(|py| {
307 let set = PyFrozenSet::new(py, [1]).unwrap();
308
309 for el in set {
310 assert_eq!(1i32, el.extract::<i32>().unwrap());
311 }
312 });
313 }
314
315 #[test]
316 fn test_frozenset_iter_bound() {
317 Python::with_gil(|py| {
318 let set = PyFrozenSet::new(py, [1]).unwrap();
319
320 for el in &set {
321 assert_eq!(1i32, el.extract::<i32>().unwrap());
322 }
323 });
324 }
325
326 #[test]
327 fn test_frozenset_iter_size_hint() {
328 Python::with_gil(|py| {
329 let set = PyFrozenSet::new(py, [1]).unwrap();
330 let mut iter = set.iter();
331
332 assert_eq!(iter.len(), 1);
334 assert_eq!(iter.size_hint(), (1, Some(1)));
335 iter.next();
336 assert_eq!(iter.len(), 0);
337 assert_eq!(iter.size_hint(), (0, Some(0)));
338 });
339 }
340
341 #[test]
342 fn test_frozenset_builder() {
343 use super::PyFrozenSetBuilder;
344
345 Python::with_gil(|py| {
346 let mut builder = PyFrozenSetBuilder::new(py).unwrap();
347
348 builder.add(1).unwrap();
350 builder.add(2).unwrap();
351 builder.add(2).unwrap();
352
353 let set = builder.finalize();
355
356 assert!(set.contains(1).unwrap());
357 assert!(set.contains(2).unwrap());
358 assert!(!set.contains(3).unwrap());
359 });
360 }
361}