1use crate::{
3 Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
4 convert::ToPyResult,
5 function::{BorrowedParam, OwnedParam, RefParam},
6 object::PyThreadingConstraint,
7};
8
9#[derive(result_like::OptionLike, is_macro::Is, Debug)]
10pub enum PySetterValue<T = PyObjectRef> {
11 Assign(T),
12 Delete,
13}
14
15impl PySetterValue {
16 pub fn unwrap_or_none(self, vm: &VirtualMachine) -> PyObjectRef {
17 match self {
18 Self::Assign(value) => value,
19 Self::Delete => vm.ctx.none(),
20 }
21 }
22}
23
24trait FromPySetterValue
25where
26 Self: Sized,
27{
28 fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult<Self>;
29}
30
31impl<T> FromPySetterValue for T
32where
33 T: Sized + TryFromObject,
34{
35 #[inline]
36 fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult<Self> {
37 let obj = obj.ok_or_else(|| vm.new_type_error("can't delete attribute"))?;
38 T::try_from_object(vm, obj)
39 }
40}
41
42impl<T> FromPySetterValue for PySetterValue<T>
43where
44 T: Sized + TryFromObject,
45{
46 #[inline]
47 fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult<Self> {
48 obj.map(|obj| T::try_from_object(vm, obj)).transpose()
49 }
50}
51
52pub(crate) type PyGetterFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine, PyObjectRef) -> PyResult)>;
53pub(crate) type PySetterFunc =
54 Box<py_dyn_fn!(dyn Fn(&VirtualMachine, PyObjectRef, PySetterValue) -> PyResult<()>)>;
55
56pub trait IntoPyGetterFunc<T>: PyThreadingConstraint + Sized + 'static {
57 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult;
58 fn into_getter(self) -> PyGetterFunc {
59 Box::new(move |vm, obj| self.get(obj, vm))
60 }
61}
62
63impl<F, T, R> IntoPyGetterFunc<(OwnedParam<T>, R, VirtualMachine)> for F
64where
65 F: Fn(T, &VirtualMachine) -> R + 'static + Send + Sync,
66 T: TryFromObject,
67 R: ToPyResult,
68{
69 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
70 let obj = T::try_from_object(vm, obj)?;
71 (self)(obj, vm).to_pyresult(vm)
72 }
73}
74
75impl<F, S, R> IntoPyGetterFunc<(BorrowedParam<S>, R, VirtualMachine)> for F
76where
77 F: Fn(&Py<S>, &VirtualMachine) -> R + 'static + Send + Sync,
78 S: PyPayload,
79 R: ToPyResult,
80{
81 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
82 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
83 (self)(&zelf, vm).to_pyresult(vm)
84 }
85}
86
87impl<F, S, R> IntoPyGetterFunc<(RefParam<S>, R, VirtualMachine)> for F
88where
89 F: Fn(&S, &VirtualMachine) -> R + 'static + Send + Sync,
90 S: PyPayload,
91 R: ToPyResult,
92{
93 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
94 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
95 (self)(&zelf, vm).to_pyresult(vm)
96 }
97}
98
99impl<F, T, R> IntoPyGetterFunc<(OwnedParam<T>, R)> for F
100where
101 F: Fn(T) -> R + 'static + Send + Sync,
102 T: TryFromObject,
103 R: ToPyResult,
104{
105 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
106 let obj = T::try_from_object(vm, obj)?;
107 (self)(obj).to_pyresult(vm)
108 }
109}
110
111impl<F, S, R> IntoPyGetterFunc<(BorrowedParam<S>, R)> for F
112where
113 F: Fn(&Py<S>) -> R + 'static + Send + Sync,
114 S: PyPayload,
115 R: ToPyResult,
116{
117 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
118 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
119 (self)(&zelf).to_pyresult(vm)
120 }
121}
122
123impl<F, S, R> IntoPyGetterFunc<(RefParam<S>, R)> for F
124where
125 F: Fn(&S) -> R + 'static + Send + Sync,
126 S: PyPayload,
127 R: ToPyResult,
128{
129 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
130 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
131 (self)(&zelf).to_pyresult(vm)
132 }
133}
134
135pub trait IntoPyNoResult {
136 fn into_noresult(self) -> PyResult<()>;
137}
138
139impl IntoPyNoResult for () {
140 #[inline]
141 fn into_noresult(self) -> PyResult<()> {
142 Ok(())
143 }
144}
145
146impl IntoPyNoResult for PyResult<()> {
147 #[inline]
148 fn into_noresult(self) -> PyResult<()> {
149 self
150 }
151}
152
153pub trait IntoPySetterFunc<T>: PyThreadingConstraint + Sized + 'static {
154 fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()>;
155 fn into_setter(self) -> PySetterFunc {
156 Box::new(move |vm, obj, value| self.set(obj, value, vm))
157 }
158}
159
160impl<F, T, V, R> IntoPySetterFunc<(OwnedParam<T>, V, R, VirtualMachine)> for F
161where
162 F: Fn(T, V, &VirtualMachine) -> R + 'static + Send + Sync,
163 T: TryFromObject,
164 V: FromPySetterValue,
165 R: IntoPyNoResult,
166{
167 fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
168 let obj = T::try_from_object(vm, obj)?;
169 let value = V::from_setter_value(vm, value)?;
170 (self)(obj, value, vm).into_noresult()
171 }
172}
173
174impl<F, S, V, R> IntoPySetterFunc<(BorrowedParam<S>, V, R, VirtualMachine)> for F
175where
176 F: Fn(&Py<S>, V, &VirtualMachine) -> R + 'static + Send + Sync,
177 S: PyPayload,
178 V: FromPySetterValue,
179 R: IntoPyNoResult,
180{
181 fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
182 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
183 let value = V::from_setter_value(vm, value)?;
184 (self)(&zelf, value, vm).into_noresult()
185 }
186}
187
188impl<F, S, V, R> IntoPySetterFunc<(RefParam<S>, V, R, VirtualMachine)> for F
189where
190 F: Fn(&S, V, &VirtualMachine) -> R + 'static + Send + Sync,
191 S: PyPayload,
192 V: FromPySetterValue,
193 R: IntoPyNoResult,
194{
195 fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
196 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
197 let value = V::from_setter_value(vm, value)?;
198 (self)(&zelf, value, vm).into_noresult()
199 }
200}
201
202impl<F, T, V, R> IntoPySetterFunc<(OwnedParam<T>, V, R)> for F
203where
204 F: Fn(T, V) -> R + 'static + Send + Sync,
205 T: TryFromObject,
206 V: FromPySetterValue,
207 R: IntoPyNoResult,
208{
209 fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
210 let obj = T::try_from_object(vm, obj)?;
211 let value = V::from_setter_value(vm, value)?;
212 (self)(obj, value).into_noresult()
213 }
214}
215
216impl<F, S, V, R> IntoPySetterFunc<(BorrowedParam<S>, V, R)> for F
217where
218 F: Fn(&Py<S>, V) -> R + 'static + Send + Sync,
219 S: PyPayload,
220 V: FromPySetterValue,
221 R: IntoPyNoResult,
222{
223 fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
224 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
225 let value = V::from_setter_value(vm, value)?;
226 (self)(&zelf, value).into_noresult()
227 }
228}
229
230impl<F, S, V, R> IntoPySetterFunc<(RefParam<S>, V, R)> for F
231where
232 F: Fn(&S, V) -> R + 'static + Send + Sync,
233 S: PyPayload,
234 V: FromPySetterValue,
235 R: IntoPyNoResult,
236{
237 fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
238 let zelf = PyRef::<S>::try_from_object(vm, obj)?;
239 let value = V::from_setter_value(vm, value)?;
240 (self)(&zelf, value).into_noresult()
241 }
242}