use std::{cmp, collections, hash, mem, ptr};
use crate::conversion::ToPyObject;
use crate::err::{self, PyErr, PyResult};
use crate::ffi;
use crate::objects::PyObject;
use crate::python::{Python, PythonObject};
pub struct PySet(PyObject);
pyobject_newtype!(PySet, PySet_Check, PySet_Type);
impl PySet {
pub fn new<I>(py: Python, iterable: I) -> PyResult<PySet>
where
I: ToPyObject,
{
iterable.with_borrowed_ptr(py, |iterable| unsafe {
err::result_cast_from_owned_ptr(py, ffi::PySet_New(iterable))
})
}
#[inline]
pub fn empty(py: Python) -> PyResult<PySet> {
unsafe { err::result_cast_from_owned_ptr(py, ffi::PySet_New(ptr::null_mut())) }
}
#[inline]
pub fn clear(&self, py: Python) -> PyResult<()> {
unsafe { err::error_on_minusone(py, ffi::PySet_Clear(self.0.as_ptr())) }
}
#[inline]
pub fn len(&self, _py: Python) -> usize {
unsafe { ffi::PySet_Size(self.0.as_ptr()) as usize }
}
pub fn contains<V>(&self, py: Python, value: V) -> PyResult<bool>
where
V: ToPyObject,
{
value.with_borrowed_ptr(py, |key| unsafe {
match ffi::PySet_Contains(self.0.as_ptr(), key) {
1 => Ok(true),
0 => Ok(false),
_ => Err(PyErr::fetch(py)),
}
})
}
pub fn add<V>(&self, py: Python, value: V) -> PyResult<()>
where
V: ToPyObject,
{
value.with_borrowed_ptr(py, |value| unsafe {
err::error_on_minusone(py, ffi::PySet_Add(self.0.as_ptr(), value))
})
}
pub fn discard<V>(&self, py: Python, value: V) -> PyResult<()>
where
V: ToPyObject,
{
value.with_borrowed_ptr(py, |value| unsafe {
err::error_on_minusone(py, ffi::PySet_Discard(self.0.as_ptr(), value))
})
}
pub fn pop(&self, py: Python) -> PyResult<PyObject> {
let as_opt =
unsafe { PyObject::from_borrowed_ptr_opt(py, ffi::PySet_Pop(self.0.as_ptr())) };
match as_opt {
None => Err(PyErr::fetch(py)),
Some(obj) => Ok(obj),
}
}
}
impl<V, H> ToPyObject for collections::HashSet<V, H>
where
V: hash::Hash + cmp::Eq + ToPyObject,
H: hash::BuildHasher,
{
type ObjectType = PySet;
fn to_py_object(&self, py: Python) -> PySet {
let set = PySet::empty(py).unwrap();
for value in self {
set.add(py, value).unwrap();
}
set
}
}
impl<V> ToPyObject for collections::BTreeSet<V>
where
V: cmp::Eq + ToPyObject,
{
type ObjectType = PySet;
fn to_py_object(&self, py: Python) -> PySet {
let set = PySet::empty(py).unwrap();
for value in self {
set.add(py, value).unwrap();
}
set
}
}
#[cfg(test)]
mod test {
use crate::conversion::ToPyObject;
use crate::objects::PySet;
use crate::python::{Python, PythonObject};
use std::collections::{BTreeSet, HashSet};
#[test]
fn test_len() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
let set = v.to_py_object(py);
assert_eq!(0, set.len(py));
v.insert(7);
let set2 = v.to_py_object(py);
assert_eq!(1, set2.len(py));
}
#[test]
fn test_contains() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
assert_eq!(true, set.contains(py, 7i32).unwrap());
assert_eq!(false, set.contains(py, 8i32).unwrap());
}
#[test]
fn test_clear() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
set.clear(py).unwrap();
assert_eq!(0, set.len(py));
assert_eq!(false, set.contains(py, 7i32).unwrap());
}
#[test]
fn test_add() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
assert!(set.add(py, 42i32).is_ok());
assert!(set.contains(py, 42i32).unwrap());
}
#[test]
fn test_add_does_not_update_original_object() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
assert!(set.add(py, 42i32).is_ok()); assert_eq!(None, v.get(&42i32)); }
#[test]
fn test_discard() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
assert!(set.discard(py, 7i32).is_ok());
assert_eq!(0, set.len(py));
assert!(!set.contains(py, 7i32).unwrap());
}
#[test]
fn test_discard_does_not_update_original_object() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
assert!(set.discard(py, 7i32).is_ok()); assert!(v.contains(&7)); }
#[test]
fn test_pop() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
let popped = set.pop(py).unwrap();
let as_int: i32 = popped.extract(py).unwrap();
assert_eq!(as_int, 7);
assert!(set.pop(py).is_err());
}
#[test]
fn test_pop_does_not_update_original_object() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = HashSet::new();
v.insert(7);
let set = v.to_py_object(py);
assert!(set.pop(py).is_ok()); assert!(v.contains(&7)); }
#[test]
fn test_btree_set() {
let gil = Python::acquire_gil();
let py = gil.python();
let mut v = BTreeSet::new();
v.insert(7);
v.insert(42);
let set = v.to_py_object(py);
assert!(set.contains(py, 7).unwrap());
assert!(set.contains(py, 42).unwrap());
assert!(!set.contains(py, 31).unwrap());
assert!(set.add(py, 31).is_ok());
assert!(!v.contains(&31));
}
}