--- 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<Revision> 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<Self> {
+ Ok(Self(obj.extract::<BaseRevision>(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,