diff --git a/rust/hg-pyo3/src/convert_cpython.rs b/rust/hg-pyo3/src/convert_cpython.rs --- a/rust/hg-pyo3/src/convert_cpython.rs +++ b/rust/hg-pyo3/src/convert_cpython.rs @@ -212,3 +212,41 @@ pub(crate) unsafe fn proxy_index_extract }; Ok(py_shared.inner) } + +/// Error propagation for an [`UnsafePyLeaked`] wrapping a [`Result`] +/// +/// TODO (will consider when implementing UnsafePyLeaked in PyO3): +/// It would be nice for UnsafePyLeaked to provide this directly as a variant +/// of the `map` method with a signature such as: +/// +/// ``` +/// unsafe fn map_or_err(&self, +/// py: Python, +/// f: impl FnOnce(T) -> Result(U, E), +/// convert_err: impl FnOnce(E) -> PyErr) +/// ``` +/// +/// This would spare users of the `cpython` crate the additional `unsafe` deref +/// to inspect the error and return it outside `UnsafePyLeaked`, and the +/// subsequent unwrapping that this function performs. +#[allow(dead_code)] +pub(crate) fn py_leaked_or_map_err( + py: cpython::Python, + leaked: cpython::UnsafePyLeaked>, + convert_err: impl FnOnce(E) -> PyErr, +) -> PyResult> { + // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` + if let Err(e) = *unsafe { + leaked + .try_borrow(py) + .map_err(|e| from_cpython_pyerr(py, e))? + } { + return Err(convert_err(e)); + } + // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` + Ok(unsafe { + leaked.map(py, |res| { + res.expect("Error case should have already be treated") + }) + }) +}