diff -r 27e773aa607d -r 4c5f6e95df84 rust/hg-cpython/src/lib.rs --- a/rust/hg-cpython/src/lib.rs Thu Aug 10 11:01:07 2023 +0200 +++ b/rust/hg-cpython/src/lib.rs Fri Aug 18 14:34:29 2023 +0200 @@ -24,6 +24,9 @@ #![allow(clippy::manual_strip)] // rust-cpython macros #![allow(clippy::type_complexity)] // rust-cpython macros +use cpython::{FromPyObject, PyInt, Python, ToPyObject}; +use hg::{BaseRevision, Revision}; + /// This crate uses nested private macros, `extern crate` is still needed in /// 2018 edition. #[macro_use] @@ -44,6 +47,40 @@ pub mod revlog; pub mod utils; +/// Revision as exposed to/from the Python layer. +/// +/// We need this indirection because of the orphan rule, meaning we can't +/// implement a foreign trait (like [`cpython::ToPyObject`]) +/// for a foreign type (like [`hg::UncheckedRevision`]). +/// +/// This also acts as a deterrent against blindly trusting Python to send +/// us valid revision numbers. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PyRevision(BaseRevision); + +impl From for PyRevision { + fn from(r: Revision) -> Self { + PyRevision(r.0) + } +} + +impl<'s> FromPyObject<'s> for PyRevision { + fn extract( + py: Python, + obj: &'s cpython::PyObject, + ) -> cpython::PyResult { + Ok(Self(obj.extract::(py)?)) + } +} + +impl ToPyObject for PyRevision { + type ObjectType = PyInt; + + fn to_py_object(&self, py: Python) -> Self::ObjectType { + self.0.to_py_object(py) + } +} + py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| { m.add( py,