discovery.rs
277 lines
| 9.6 KiB
| application/rls-services+xml
|
RustLexer
Georges Racinet
|
r42356 | // discovery.rs | ||
// | ||||
// Copyright 2018 Georges Racinet <gracinet@anybox.fr> | ||||
// | ||||
// This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | ||||
//! Bindings for the `hg::discovery` module provided by the | ||||
//! `hg-core` crate. From Python, this will be seen as `rustext.discovery` | ||||
//! | ||||
//! # Classes visible from Python: | ||||
Georges Racinet
|
r52078 | //! - [`PartialDiscovery`] is the Rust implementation of | ||
Georges Racinet
|
r42356 | //! `mercurial.setdiscovery.partialdiscovery`. | ||
Raphaël Gomès
|
r51872 | use crate::PyRevision; | ||
Raphaël Gomès
|
r42828 | use crate::{ | ||
Georges Racinet
|
r52138 | conversion::rev_pyiter_collect, exceptions::GraphError, | ||
revlog::PySharedIndex, | ||||
Raphaël Gomès
|
r42828 | }; | ||
Georges Racinet
|
r42357 | use cpython::{ | ||
Raphaël Gomès
|
r51872 | ObjectProtocol, PyClone, PyDict, PyModule, PyObject, PyResult, PyTuple, | ||
Georges Racinet
|
r52138 | Python, PythonObject, ToPyObject, UnsafePyLeaked, | ||
Georges Racinet
|
r42357 | }; | ||
Georges Racinet
|
r42356 | use hg::discovery::PartialDiscovery as CorePartialDiscovery; | ||
use hg::Revision; | ||||
Georges Racinet
|
r43563 | use std::collections::HashSet; | ||
Georges Racinet
|
r42356 | |||
use std::cell::RefCell; | ||||
Georges Racinet
|
r52138 | use crate::revlog::py_rust_index_to_graph; | ||
r44398 | ||||
Georges Racinet
|
r42356 | py_class!(pub class PartialDiscovery |py| { | ||
Georges Racinet
|
r52138 | data inner: RefCell<UnsafePyLeaked<CorePartialDiscovery<PySharedIndex>>>; | ||
data index: RefCell<UnsafePyLeaked<PySharedIndex>>; | ||||
Georges Racinet
|
r42356 | |||
Georges Racinet
|
r42963 | // `_respectsize` is currently only here to replicate the Python API and | ||
// will be used in future patches inside methods that are yet to be | ||||
// implemented. | ||||
Georges Racinet
|
r42356 | def __new__( | ||
_cls, | ||||
Georges Racinet
|
r42964 | repo: PyObject, | ||
Georges Racinet
|
r42963 | targetheads: PyObject, | ||
Georges Racinet
|
r42968 | respectsize: bool, | ||
randomize: bool = true | ||||
Georges Racinet
|
r42356 | ) -> PyResult<PartialDiscovery> { | ||
Georges Racinet
|
r52135 | Self::inner_new(py, repo, targetheads, respectsize, randomize) | ||
Georges Racinet
|
r42356 | } | ||
def addcommons(&self, commons: PyObject) -> PyResult<PyObject> { | ||||
Georges Racinet
|
r52135 | self.inner_addcommons(py, commons) | ||
} | ||||
Georges Racinet
|
r42356 | |||
def addmissings(&self, missings: PyObject) -> PyResult<PyObject> { | ||||
Georges Racinet
|
r52135 | self.inner_addmissings(py, missings) | ||
Georges Racinet
|
r42356 | } | ||
def addinfo(&self, sample: PyObject) -> PyResult<PyObject> { | ||||
Georges Racinet
|
r52135 | self.inner_addinfo(py, sample) | ||
Georges Racinet
|
r42356 | } | ||
def hasinfo(&self) -> PyResult<bool> { | ||||
Georges Racinet
|
r52138 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let inner = unsafe { leaked.try_borrow(py)? }; | ||
Ok(inner.has_info()) | ||||
Georges Racinet
|
r42356 | } | ||
def iscomplete(&self) -> PyResult<bool> { | ||||
Georges Racinet
|
r52138 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let inner = unsafe { leaked.try_borrow(py)? }; | ||
Ok(inner.is_complete()) | ||||
Georges Racinet
|
r42356 | } | ||
Georges Racinet
|
r42357 | def stats(&self) -> PyResult<PyDict> { | ||
Georges Racinet
|
r52138 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let inner = unsafe { leaked.try_borrow(py)? }; | ||
let stats = inner.stats(); | ||||
Georges Racinet
|
r42357 | let as_dict: PyDict = PyDict::new(py); | ||
as_dict.set_item(py, "undecided", | ||||
Georges Racinet
|
r42519 | stats.undecided.map( | ||
|l| l.to_py_object(py).into_object()) | ||||
.unwrap_or_else(|| py.None()))?; | ||||
Georges Racinet
|
r42357 | Ok(as_dict) | ||
} | ||||
Raphaël Gomès
|
r51872 | def commonheads(&self) -> PyResult<HashSet<PyRevision>> { | ||
Georges Racinet
|
r52138 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let inner = unsafe { leaked.try_borrow(py)? }; | ||
let res = inner.common_heads() | ||||
Raphaël Gomès
|
r51872 | .map_err(|e| GraphError::pynew(py, e))?; | ||
Ok(res.into_iter().map(Into::into).collect()) | ||||
Georges Racinet
|
r42356 | } | ||
Georges Racinet
|
r42967 | |||
Georges Racinet
|
r52135 | def takefullsample(&self, headrevs: PyObject, | ||
Georges Racinet
|
r42967 | size: usize) -> PyResult<PyObject> { | ||
Georges Racinet
|
r52135 | self.inner_takefullsample(py, headrevs, size) | ||
} | ||||
def takequicksample(&self, headrevs: PyObject, | ||||
size: usize) -> PyResult<PyObject> { | ||||
self.inner_takequicksample(py, headrevs, size) | ||||
} | ||||
}); | ||||
impl PartialDiscovery { | ||||
fn inner_new( | ||||
py: Python, | ||||
repo: PyObject, | ||||
targetheads: PyObject, | ||||
respectsize: bool, | ||||
randomize: bool, | ||||
) -> PyResult<Self> { | ||||
let index = repo.getattr(py, "changelog")?.getattr(py, "index")?; | ||||
Georges Racinet
|
r52138 | let cloned_index = py_rust_index_to_graph(py, index.clone_ref(py))?; | ||
let index = py_rust_index_to_graph(py, index)?; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let target_heads = { | ||
let borrowed_idx = unsafe { index.try_borrow(py)? }; | ||||
rev_pyiter_collect(py, &targetheads, &*borrowed_idx)? | ||||
}; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let lazy_disco = unsafe { | ||
index.map(py, |idx| { | ||||
CorePartialDiscovery::new( | ||||
idx, | ||||
target_heads, | ||||
respectsize, | ||||
randomize, | ||||
) | ||||
}) | ||||
}; | ||||
Georges Racinet
|
r52135 | Self::create_instance( | ||
py, | ||||
Georges Racinet
|
r52138 | RefCell::new(lazy_disco), | ||
RefCell::new(cloned_index), | ||||
Georges Racinet
|
r52135 | ) | ||
} | ||||
Georges Racinet
|
r52136 | /// Convert a Python iterator of revisions into a vector | ||
fn pyiter_to_vec( | ||||
&self, | ||||
py: Python, | ||||
iter: &PyObject, | ||||
) -> PyResult<Vec<Revision>> { | ||||
Georges Racinet
|
r52138 | let leaked = self.index(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let index = unsafe { leaked.try_borrow(py)? }; | ||
Georges Racinet
|
r52136 | rev_pyiter_collect(py, iter, &*index) | ||
} | ||||
Georges Racinet
|
r52135 | fn inner_addinfo( | ||
&self, | ||||
py: Python, | ||||
sample: PyObject, | ||||
) -> PyResult<PyObject> { | ||||
let mut missing: Vec<Revision> = Vec::new(); | ||||
let mut common: Vec<Revision> = Vec::new(); | ||||
for info in sample.iter(py)? { | ||||
// info is a pair (Revision, bool) | ||||
let mut revknown = info?.iter(py)?; | ||||
let rev: PyRevision = revknown.next().unwrap()?.extract(py)?; | ||||
// This is fine since we're just using revisions as integers | ||||
// for the purposes of discovery | ||||
let rev = Revision(rev.0); | ||||
let known: bool = revknown.next().unwrap()?.extract(py)?; | ||||
if known { | ||||
common.push(rev); | ||||
} else { | ||||
missing.push(rev); | ||||
} | ||||
} | ||||
Georges Racinet
|
r52138 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let mut inner = unsafe { leaked.try_borrow_mut(py)? }; | ||
Georges Racinet
|
r52135 | inner | ||
.add_common_revisions(common) | ||||
.map_err(|e| GraphError::pynew(py, e))?; | ||||
inner | ||||
.add_missing_revisions(missing) | ||||
.map_err(|e| GraphError::pynew(py, e))?; | ||||
Ok(py.None()) | ||||
} | ||||
fn inner_addcommons( | ||||
&self, | ||||
py: Python, | ||||
commons: PyObject, | ||||
) -> PyResult<PyObject> { | ||||
Georges Racinet
|
r52136 | let commons_vec = self.pyiter_to_vec(py, &commons)?; | ||
Georges Racinet
|
r52138 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let mut inner = unsafe { leaked.try_borrow_mut(py)? }; | ||
Georges Racinet
|
r52135 | inner | ||
.add_common_revisions(commons_vec) | ||||
.map_err(|e| GraphError::pynew(py, e))?; | ||||
Ok(py.None()) | ||||
} | ||||
fn inner_addmissings( | ||||
&self, | ||||
py: Python, | ||||
missings: PyObject, | ||||
) -> PyResult<PyObject> { | ||||
Georges Racinet
|
r52136 | let missings_vec = self.pyiter_to_vec(py, &missings)?; | ||
Georges Racinet
|
r52138 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let mut inner = unsafe { leaked.try_borrow_mut(py)? }; | ||
Georges Racinet
|
r52135 | inner | ||
.add_missing_revisions(missings_vec) | ||||
.map_err(|e| GraphError::pynew(py, e))?; | ||||
Ok(py.None()) | ||||
} | ||||
fn inner_takefullsample( | ||||
&self, | ||||
py: Python, | ||||
_headrevs: PyObject, | ||||
size: usize, | ||||
) -> PyResult<PyObject> { | ||||
Georges Racinet
|
r52138 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let mut inner = unsafe { leaked.try_borrow_mut(py)? }; | ||
Georges Racinet
|
r52135 | let sample = inner | ||
.take_full_sample(size) | ||||
Georges Racinet
|
r42967 | .map_err(|e| GraphError::pynew(py, e))?; | ||
let as_vec: Vec<PyObject> = sample | ||||
.iter() | ||||
Raphaël Gomès
|
r51872 | .map(|rev| PyRevision(rev.0).to_py_object(py).into_object()) | ||
Georges Racinet
|
r42967 | .collect(); | ||
Ok(PyTuple::new(py, as_vec.as_slice()).into_object()) | ||||
} | ||||
Georges Racinet
|
r52135 | fn inner_takequicksample( | ||
&self, | ||||
py: Python, | ||||
headrevs: PyObject, | ||||
size: usize, | ||||
) -> PyResult<PyObject> { | ||||
Georges Racinet
|
r52136 | let revsvec = self.pyiter_to_vec(py, &headrevs)?; | ||
Georges Racinet
|
r52138 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52138 | let mut inner = unsafe { leaked.try_borrow_mut(py)? }; | ||
Georges Racinet
|
r52135 | let sample = inner | ||
.take_quick_sample(revsvec, size) | ||||
Georges Racinet
|
r42967 | .map_err(|e| GraphError::pynew(py, e))?; | ||
let as_vec: Vec<PyObject> = sample | ||||
.iter() | ||||
Raphaël Gomès
|
r51872 | .map(|rev| PyRevision(rev.0).to_py_object(py).into_object()) | ||
Georges Racinet
|
r42967 | .collect(); | ||
Ok(PyTuple::new(py, as_vec.as_slice()).into_object()) | ||||
} | ||||
Georges Racinet
|
r52135 | } | ||
Georges Racinet
|
r42356 | |||
/// Create the module, with __package__ given from parent | ||||
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | ||||
let dotted_name = &format!("{}.discovery", package); | ||||
let m = PyModule::new(py, dotted_name)?; | ||||
m.add(py, "__package__", package)?; | ||||
m.add( | ||||
py, | ||||
"__doc__", | ||||
"Discovery of common node sets - Rust implementation", | ||||
)?; | ||||
m.add_class::<PartialDiscovery>(py)?; | ||||
let sys = PyModule::import(py, "sys")?; | ||||
let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; | ||||
sys_modules.set_item(py, dotted_name, &m)?; | ||||
// Example C code (see pyexpat.c and import.c) will "give away the | ||||
// reference", but we won't because it will be consumed once the | ||||
// Rust PyObject is dropped. | ||||
Ok(m) | ||||
} | ||||