/*! Python `attribute` descriptor class. (PyGetSet) */ use crate::{ convert::ToPyResult, function::{OwnedParam, RefParam}, object::PyThreadingConstraint, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; #[derive(result_like::OptionLike, is_macro::Is, Debug)] pub enum PySetterValue { Assign(T), Delete, } impl PySetterValue { pub fn unwrap_or_none(self, vm: &VirtualMachine) -> PyObjectRef { match self { Self::Assign(value) => value, Self::Delete => vm.ctx.none(), } } } trait FromPySetterValue where Self: Sized, { fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult; } impl FromPySetterValue for T where T: Sized + TryFromObject, { #[inline] fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult { let obj = obj.ok_or_else(|| vm.new_type_error("can't delete attribute".to_owned()))?; T::try_from_object(vm, obj) } } impl FromPySetterValue for PySetterValue where T: Sized + TryFromObject, { #[inline] fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult { obj.map(|obj| T::try_from_object(vm, obj)).transpose() } } pub type PyGetterFunc = Box PyResult)>; pub type PySetterFunc = Box PyResult<()>)>; pub trait IntoPyGetterFunc: PyThreadingConstraint + Sized + 'static { fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult; fn into_getter(self) -> PyGetterFunc { Box::new(move |vm, obj| self.get(obj, vm)) } } impl IntoPyGetterFunc<(OwnedParam, R, VirtualMachine)> for F where F: Fn(T, &VirtualMachine) -> R + 'static + Send + Sync, T: TryFromObject, R: ToPyResult, { fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let obj = T::try_from_object(vm, obj)?; (self)(obj, vm).to_pyresult(vm) } } impl IntoPyGetterFunc<(RefParam, R, VirtualMachine)> for F where F: Fn(&S, &VirtualMachine) -> R + 'static + Send + Sync, S: PyPayload, R: ToPyResult, { fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let zelf = PyRef::::try_from_object(vm, obj)?; (self)(&zelf, vm).to_pyresult(vm) } } impl IntoPyGetterFunc<(OwnedParam, R)> for F where F: Fn(T) -> R + 'static + Send + Sync, T: TryFromObject, R: ToPyResult, { fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let obj = T::try_from_object(vm, obj)?; (self)(obj).to_pyresult(vm) } } impl IntoPyGetterFunc<(RefParam, R)> for F where F: Fn(&S) -> R + 'static + Send + Sync, S: PyPayload, R: ToPyResult, { fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let zelf = PyRef::::try_from_object(vm, obj)?; (self)(&zelf).to_pyresult(vm) } } pub trait IntoPyNoResult { fn into_noresult(self) -> PyResult<()>; } impl IntoPyNoResult for () { #[inline] fn into_noresult(self) -> PyResult<()> { Ok(()) } } impl IntoPyNoResult for PyResult<()> { #[inline] fn into_noresult(self) -> PyResult<()> { self } } pub trait IntoPySetterFunc: PyThreadingConstraint + Sized + 'static { fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()>; fn into_setter(self) -> PySetterFunc { Box::new(move |vm, obj, value| self.set(obj, value, vm)) } } impl IntoPySetterFunc<(OwnedParam, V, R, VirtualMachine)> for F where F: Fn(T, V, &VirtualMachine) -> R + 'static + Send + Sync, T: TryFromObject, V: FromPySetterValue, R: IntoPyNoResult, { fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> { let obj = T::try_from_object(vm, obj)?; let value = V::from_setter_value(vm, value)?; (self)(obj, value, vm).into_noresult() } } impl IntoPySetterFunc<(RefParam, V, R, VirtualMachine)> for F where F: Fn(&S, V, &VirtualMachine) -> R + 'static + Send + Sync, S: PyPayload, V: FromPySetterValue, R: IntoPyNoResult, { fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> { let zelf = PyRef::::try_from_object(vm, obj)?; let value = V::from_setter_value(vm, value)?; (self)(&zelf, value, vm).into_noresult() } } impl IntoPySetterFunc<(OwnedParam, V, R)> for F where F: Fn(T, V) -> R + 'static + Send + Sync, T: TryFromObject, V: FromPySetterValue, R: IntoPyNoResult, { fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> { let obj = T::try_from_object(vm, obj)?; let value = V::from_setter_value(vm, value)?; (self)(obj, value).into_noresult() } } impl IntoPySetterFunc<(RefParam, V, R)> for F where F: Fn(&S, V) -> R + 'static + Send + Sync, S: PyPayload, V: FromPySetterValue, R: IntoPyNoResult, { fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> { let zelf = PyRef::::try_from_object(vm, obj)?; let value = V::from_setter_value(vm, value)?; (self)(&zelf, value).into_noresult() } }