1use std::{cmp, collections, hash};
2
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6 conversion::IntoPyObject,
7 instance::Bound,
8 types::{
9 any::PyAnyMethods,
10 frozenset::PyFrozenSetMethods,
11 set::{new_from_iter, try_new_from_iter, PySetMethods},
12 PyFrozenSet, PySet,
13 },
14 FromPyObject, PyAny, PyErr, PyObject, PyResult, Python,
15};
16#[allow(deprecated)]
17use crate::{IntoPy, ToPyObject};
18
19#[allow(deprecated)]
20impl<T, S> ToPyObject for collections::HashSet<T, S>
21where
22 T: hash::Hash + Eq + ToPyObject,
23 S: hash::BuildHasher + Default,
24{
25 fn to_object(&self, py: Python<'_>) -> PyObject {
26 new_from_iter(py, self)
27 .expect("Failed to create Python set from HashSet")
28 .into()
29 }
30}
31
32#[allow(deprecated)]
33impl<T> ToPyObject for collections::BTreeSet<T>
34where
35 T: hash::Hash + Eq + ToPyObject,
36{
37 fn to_object(&self, py: Python<'_>) -> PyObject {
38 new_from_iter(py, self)
39 .expect("Failed to create Python set from BTreeSet")
40 .into()
41 }
42}
43
44#[allow(deprecated)]
45impl<K, S> IntoPy<PyObject> for collections::HashSet<K, S>
46where
47 K: IntoPy<PyObject> + Eq + hash::Hash,
48 S: hash::BuildHasher + Default,
49{
50 fn into_py(self, py: Python<'_>) -> PyObject {
51 new_from_iter(py, self.into_iter().map(|item| item.into_py(py)))
52 .expect("Failed to create Python set from HashSet")
53 .into()
54 }
55}
56
57impl<'py, K, S> IntoPyObject<'py> for collections::HashSet<K, S>
58where
59 K: IntoPyObject<'py> + Eq + hash::Hash,
60 S: hash::BuildHasher + Default,
61{
62 type Target = PySet;
63 type Output = Bound<'py, Self::Target>;
64 type Error = PyErr;
65
66 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
67 try_new_from_iter(py, self)
68 }
69
70 #[cfg(feature = "experimental-inspect")]
71 fn type_output() -> TypeInfo {
72 TypeInfo::set_of(K::type_output())
73 }
74}
75
76impl<'a, 'py, K, H> IntoPyObject<'py> for &'a collections::HashSet<K, H>
77where
78 &'a K: IntoPyObject<'py> + Eq + hash::Hash,
79 K: 'a, H: hash::BuildHasher,
81{
82 type Target = PySet;
83 type Output = Bound<'py, Self::Target>;
84 type Error = PyErr;
85
86 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
87 try_new_from_iter(py, self.iter())
88 }
89
90 #[cfg(feature = "experimental-inspect")]
91 fn type_output() -> TypeInfo {
92 TypeInfo::set_of(<&K>::type_output())
93 }
94}
95
96impl<'py, K, S> FromPyObject<'py> for collections::HashSet<K, S>
97where
98 K: FromPyObject<'py> + cmp::Eq + hash::Hash,
99 S: hash::BuildHasher + Default,
100{
101 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
102 match ob.downcast::<PySet>() {
103 Ok(set) => set.iter().map(|any| any.extract()).collect(),
104 Err(err) => {
105 if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
106 frozen_set.iter().map(|any| any.extract()).collect()
107 } else {
108 Err(PyErr::from(err))
109 }
110 }
111 }
112 }
113
114 #[cfg(feature = "experimental-inspect")]
115 fn type_input() -> TypeInfo {
116 TypeInfo::set_of(K::type_input())
117 }
118}
119
120#[allow(deprecated)]
121impl<K> IntoPy<PyObject> for collections::BTreeSet<K>
122where
123 K: IntoPy<PyObject> + cmp::Ord,
124{
125 fn into_py(self, py: Python<'_>) -> PyObject {
126 new_from_iter(py, self.into_iter().map(|item| item.into_py(py)))
127 .expect("Failed to create Python set from BTreeSet")
128 .into()
129 }
130}
131
132impl<'py, K> IntoPyObject<'py> for collections::BTreeSet<K>
133where
134 K: IntoPyObject<'py> + cmp::Ord,
135{
136 type Target = PySet;
137 type Output = Bound<'py, Self::Target>;
138 type Error = PyErr;
139
140 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
141 try_new_from_iter(py, self)
142 }
143
144 #[cfg(feature = "experimental-inspect")]
145 fn type_output() -> TypeInfo {
146 TypeInfo::set_of(K::type_output())
147 }
148}
149
150impl<'a, 'py, K> IntoPyObject<'py> for &'a collections::BTreeSet<K>
151where
152 &'a K: IntoPyObject<'py> + cmp::Ord,
153 K: 'a,
154{
155 type Target = PySet;
156 type Output = Bound<'py, Self::Target>;
157 type Error = PyErr;
158
159 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
160 try_new_from_iter(py, self.iter())
161 }
162
163 #[cfg(feature = "experimental-inspect")]
164 fn type_output() -> TypeInfo {
165 TypeInfo::set_of(<&K>::type_output())
166 }
167}
168
169impl<'py, K> FromPyObject<'py> for collections::BTreeSet<K>
170where
171 K: FromPyObject<'py> + cmp::Ord,
172{
173 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
174 match ob.downcast::<PySet>() {
175 Ok(set) => set.iter().map(|any| any.extract()).collect(),
176 Err(err) => {
177 if let Ok(frozen_set) = ob.downcast::<PyFrozenSet>() {
178 frozen_set.iter().map(|any| any.extract()).collect()
179 } else {
180 Err(PyErr::from(err))
181 }
182 }
183 }
184 }
185
186 #[cfg(feature = "experimental-inspect")]
187 fn type_input() -> TypeInfo {
188 TypeInfo::set_of(K::type_input())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use crate::types::{any::PyAnyMethods, PyFrozenSet, PySet};
195 use crate::{IntoPyObject, PyObject, Python};
196 use std::collections::{BTreeSet, HashSet};
197
198 #[test]
199 fn test_extract_hashset() {
200 Python::with_gil(|py| {
201 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
202 let hash_set: HashSet<usize> = set.extract().unwrap();
203 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
204
205 let set = PyFrozenSet::new(py, [1, 2, 3, 4, 5]).unwrap();
206 let hash_set: HashSet<usize> = set.extract().unwrap();
207 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
208 });
209 }
210
211 #[test]
212 fn test_extract_btreeset() {
213 Python::with_gil(|py| {
214 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
215 let hash_set: BTreeSet<usize> = set.extract().unwrap();
216 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
217
218 let set = PyFrozenSet::new(py, [1, 2, 3, 4, 5]).unwrap();
219 let hash_set: BTreeSet<usize> = set.extract().unwrap();
220 assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
221 });
222 }
223
224 #[test]
225 #[allow(deprecated)]
226 fn test_set_into_py() {
227 use crate::IntoPy;
228 Python::with_gil(|py| {
229 let bt: BTreeSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
230 let hs: HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
231
232 let bto: PyObject = bt.clone().into_py(py);
233 let hso: PyObject = hs.clone().into_py(py);
234
235 assert_eq!(bt, bto.extract(py).unwrap());
236 assert_eq!(hs, hso.extract(py).unwrap());
237 });
238 }
239
240 #[test]
241 fn test_set_into_pyobject() {
242 Python::with_gil(|py| {
243 let bt: BTreeSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
244 let hs: HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
245
246 let bto = (&bt).into_pyobject(py).unwrap();
247 let hso = (&hs).into_pyobject(py).unwrap();
248
249 assert_eq!(bt, bto.extract().unwrap());
250 assert_eq!(hs, hso.extract().unwrap());
251 });
252 }
253}