use std::marker::PhantomData; use std::ops::Deref; use crate::obj::objtype; use crate::pyobject::{ IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; // TODO: Move PyFuncArgs, FromArgs, etc. here // TODO: `PyRef` probably actually belongs in the pyobject module. /// A reference to the payload of a built-in object. /// /// Note that a `PyRef` can only deref to a shared / immutable reference. /// It is the payload type's responsibility to handle (possibly concurrent) /// mutability with locks or concurrent data structures if required. /// /// A `PyRef` can be directly returned from a built-in function to handle /// situations (such as when implementing in-place methods such as `__iadd__`) /// where a reference to the same object must be returned. pub struct PyRef { // invariant: this obj must always have payload of type T obj: PyObjectRef, _payload: PhantomData, } impl PyRef where T: PyObjectPayload2, { pub fn new(ctx: &PyContext, payload: T) -> Self { PyRef { obj: PyObject::new( PyObjectPayload::AnyRustValue { value: Box::new(payload), }, T::required_type(ctx), ), _payload: PhantomData, } } } impl Deref for PyRef where T: PyObjectPayload2, { type Target = T; fn deref(&self) -> &T { self.obj.payload().expect("unexpected payload for type") } } impl TryFromObject for PyRef where T: PyObjectPayload2, { fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult { if objtype::isinstance(&obj, &T::required_type(&vm.ctx)) { Ok(PyRef { obj, _payload: PhantomData, }) } else { let expected_type = vm.to_pystr(&T::required_type(&vm.ctx))?; let actual_type = vm.to_pystr(&obj.typ())?; Err(vm.new_type_error(format!( "Expected type {}, not {}", expected_type, actual_type, ))) } } } impl IntoPyObject for PyRef { fn into_pyobject(self, _ctx: &PyContext) -> PyResult { Ok(self.obj) } }