1use std::{cmp, collections, hash};
2
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6 instance::Bound,
7 types::any::PyAnyMethods,
8 types::frozenset::PyFrozenSetMethods,
9 types::set::{new_from_iter, PySetMethods},
10 types::{PyFrozenSet, PySet},
11 FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
12};
13
14impl<T, S> ToPyObject for collections::HashSet<T, S>
15where
16 T: hash::Hash + Eq + ToPyObject,
17 S: hash::BuildHasher + Default,
18{
19 fn to_object(&self, py: Python<'_>) -> PyObject {
20 new_from_iter(py, self)
21 .expect("Failed to create Python set from HashSet")
22 .into()
23 }
24}
25
26impl<T> ToPyObject for collections::BTreeSet<T>
27where
28 T: hash::Hash + Eq + ToPyObject,
29{
30 fn to_object(&self, py: Python<'_>) -> PyObject {
31 new_from_iter(py, self)
32 .expect("Failed to create Python set from BTreeSet")
33 .into()
34 }
35}
36
37impl<K, S> IntoPy<PyObject> for collections::HashSet<K, S>
38where
39 K: IntoPy<PyObject> + Eq + hash::Hash,
40 S: hash::BuildHasher + Default,
41{
42 fn into_py(self, py: Python<'_>) -> PyObject {
43 new_from_iter(py, self.into_iter().map(|item| item.into_py(py)))
44 .expect("Failed to create Python set from HashSet")
45 .into()
46 }
47
48 #[cfg(feature = "experimental-inspect")]
49 fn type_output() -> TypeInfo {
50 TypeInfo::set_of(K::type_output())
51 }
52}
53
54impl<'py, K, S> FromPyObject<'py> for collections::HashSet<K, S>
55where
56 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
57 S: hash::BuildHasher + Default,
58{
59 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
60 match ob.downcast::<PySet>() {
61 Ok(set) => set.iter().map(|any| any.extract()).collect(),
62 Err(err) => {
63 if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
64 frozen_set.iter().map(|any| any.extract()).collect()
65 } else {
66 Err(PyErr::from(err))
67 }
68 }
69 }
70 }
71
72 #[cfg(feature = "experimental-inspect")]
73 fn type_input() -> TypeInfo {
74 TypeInfo::set_of(K::type_input())
75 }
76}
77
78impl<K> IntoPy<PyObject> for collections::BTreeSet<K>
79where
80 K: IntoPy<PyObject> + cmp::Ord,
81{
82 fn into_py(self, py: Python<'_>) -> PyObject {
83 new_from_iter(py, self.into_iter().map(|item| item.into_py(py)))
84 .expect("Failed to create Python set from BTreeSet")
85 .into()
86 }
87
88 #[cfg(feature = "experimental-inspect")]
89 fn type_output() -> TypeInfo {
90 TypeInfo::set_of(K::type_output())
91 }
92}
93
94impl<'py, K> FromPyObject<'py> for collections::BTreeSet<K>
95where
96 K: FromPyObject<'py> + cmp::Ord,
97{
98 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
99 match ob.downcast::<PySet>() {
100 Ok(set) => set.iter().map(|any| any.extract()).collect(),
101 Err(err) => {
102 if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
103 frozen_set.iter().map(|any| any.extract()).collect()
104 } else {
105 Err(PyErr::from(err))
106 }
107 }
108 }
109 }
110
111 #[cfg(feature = "experimental-inspect")]
112 fn type_input() -> TypeInfo {
113 TypeInfo::set_of(K::type_input())
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use crate::types::{any::PyAnyMethods, PyFrozenSet, PySet};
120 use crate::{IntoPy, PyObject, Python, ToPyObject};
121 use std::collections::{BTreeSet, HashSet};
122
123 #[test]
124 fn test_extract_hashset() {
125 Python::with_gil(|py| {
126 let set = PySet::new_bound(py, &[1, 2, 3, 4, 5]).unwrap();
127 let hash_set: HashSet<usize> = set.extract().unwrap();
128 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
129
130 let set = PyFrozenSet::new_bound(py, &[1, 2, 3, 4, 5]).unwrap();
131 let hash_set: HashSet<usize> = set.extract().unwrap();
132 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
133 });
134 }
135
136 #[test]
137 fn test_extract_btreeset() {
138 Python::with_gil(|py| {
139 let set = PySet::new_bound(py, &[1, 2, 3, 4, 5]).unwrap();
140 let hash_set: BTreeSet<usize> = set.extract().unwrap();
141 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
142
143 let set = PyFrozenSet::new_bound(py, &[1, 2, 3, 4, 5]).unwrap();
144 let hash_set: BTreeSet<usize> = set.extract().unwrap();
145 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
146 });
147 }
148
149 #[test]
150 fn test_set_into_py() {
151 Python::with_gil(|py| {
152 let bt: BTreeSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
153 let hs: HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
154
155 let bto: PyObject = bt.clone().into_py(py);
156 let hso: PyObject = hs.clone().into_py(py);
157
158 assert_eq!(bt, bto.extract(py).unwrap());
159 assert_eq!(hs, hso.extract(py).unwrap());
160 });
161 }
162
163 #[test]
164 fn test_set_to_object() {
165 Python::with_gil(|py| {
166 let bt: BTreeSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
167 let hs: HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
168
169 let bto: PyObject = bt.to_object(py);
170 let hso: PyObject = hs.to_object(py);
171
172 assert_eq!(bt, bto.extract(py).unwrap());
173 assert_eq!(hs, hso.extract(py).unwrap());
174 });
175 }
176}