use crate::{
    object::{AsObject, PyObject, PyPayload, PyRef, PyResult},
    vm::VirtualMachine,
};

/// Marks a type that has the exact same layout as PyObjectRef, e.g. a type that is
/// `repr(transparent)` over PyObjectRef.
///
/// # Safety
/// Can only be implemented for types that are `repr(transparent)` over a PyObjectRef `obj`,
/// and logically valid so long as `check(vm, obj)` returns `Ok(())`
pub unsafe trait TransmuteFromObject: Sized {
    fn check(vm: &VirtualMachine, obj: &PyObject) -> PyResult<()>;
}

unsafe impl<T: PyPayload> TransmuteFromObject for PyRef<T> {
    fn check(vm: &VirtualMachine, obj: &PyObject) -> PyResult<()> {
        let class = T::class(&vm.ctx);
        if obj.fast_isinstance(class) {
            if obj.downcastable::<T>() {
                Ok(())
            } else {
                Err(vm.new_downcast_runtime_error(class, obj))
            }
        } else {
            Err(vm.new_downcast_type_error(class, obj))
        }
    }
}
