rust/hg-cpython/src/dagops.rs
author Georges Racinet <georges.racinet@octobus.net>
Sun, 29 Oct 2023 10:47:54 +0100
changeset 51241 578c049f0408
parent 50979 4c5f6e95df84
child 51255 24d3298189d7
permissions -rw-r--r--
rust-index: using `hg::index::Index` in `hg-cpython::dagops` Hooking `headrevs` to the Rust index is straightforward as long as we go the `PySharedRef` way. Direct attempts of obtaining a reference to the inner `hg::index::Index` fail for lifetime reasons: the reference is bound to the GIL, yet the `as_set` local variable is considered to be static (the borrow checker clearly does not realize or care that this set only stores `Revision` values). In `rank()`, the chosen solution is the simplest as far as `hg-cpython` is concerned, but it has the defect of removing an implementation that would be easily adaptable if the core index did implement `RankedGraph` (returning the same error as long as only `REVLOGV1` is supported), but that would introduce a direct dependency of `hg-core` on the ``vcsgraph` crate.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     1
// dagops.rs
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     2
//
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     3
// Copyright 2019 Georges Racinet <georges.racinet@octobus.net>
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     4
//
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     5
// This software may be used and distributed according to the terms of the
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     6
// GNU General Public License version 2 or any later version.
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     7
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     8
//! Bindings for the `hg::dagops` module provided by the
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
     9
//! `hg-core` package.
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    10
//!
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    11
//! From Python, this will be seen as `mercurial.rustext.dagop`
50979
4c5f6e95df84 rust: make `Revision` a newtype
Raphaël Gomès <rgomes@octobus.net>
parents: 48854
diff changeset
    12
use crate::PyRevision;
43945
f98f0e3ddaa1 rust-index: add a function to convert PyObject index for hg-core
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43269
diff changeset
    13
use crate::{conversion::rev_pyiter_collect, exceptions::GraphError};
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    14
use cpython::{PyDict, PyModule, PyObject, PyResult, Python};
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    15
use hg::dagops;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    16
use hg::Revision;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    17
use std::collections::HashSet;
51241
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    18
use vcsgraph::graph::Rank;
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    19
51241
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    20
use crate::revlog::py_rust_index_to_graph;
43945
f98f0e3ddaa1 rust-index: add a function to convert PyObject index for hg-core
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 43269
diff changeset
    21
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    22
/// Using the the `index`, return heads out of any Python iterable of Revisions
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    23
///
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    24
/// This is the Rust counterpart for `mercurial.dagop.headrevs`
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    25
pub fn headrevs(
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    26
    py: Python,
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    27
    index: PyObject,
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    28
    revs: PyObject,
50979
4c5f6e95df84 rust: make `Revision` a newtype
Raphaël Gomès <rgomes@octobus.net>
parents: 48854
diff changeset
    29
) -> PyResult<HashSet<PyRevision>> {
51241
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    30
    let py_leaked = py_rust_index_to_graph(py, index)?;
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    31
    let index = &*unsafe { py_leaked.try_borrow(py)? };
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    32
    let mut as_set: HashSet<Revision> = rev_pyiter_collect(py, &revs, index)?;
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    33
    dagops::retain_heads(index, &mut as_set)
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    34
        .map_err(|e| GraphError::pynew(py, e))?;
50979
4c5f6e95df84 rust: make `Revision` a newtype
Raphaël Gomès <rgomes@octobus.net>
parents: 48854
diff changeset
    35
    Ok(as_set.into_iter().map(Into::into).collect())
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    36
}
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    37
48854
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    38
/// Computes the rank, i.e. the number of ancestors including itself,
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    39
/// of a node represented by its parents.
51241
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    40
///
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    41
/// Currently, the pure Rust index supports only the REVLOGV1 format, hence
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    42
/// the only possible return value is that the rank is unknown.
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    43
///
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    44
/// References:
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    45
/// - C implementation, function `index_fast_rank()`.
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    46
/// - `impl vcsgraph::graph::RankedGraph for Index` in `crate::cindex`.
48854
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    47
pub fn rank(
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    48
    py: Python,
51241
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    49
    _index: PyObject,
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    50
    _p1r: PyRevision,
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    51
    _p2r: PyRevision,
48854
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    52
) -> PyResult<Rank> {
51241
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    53
    Err(GraphError::pynew_from_vcsgraph(
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    54
        py,
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    55
        vcsgraph::graph::GraphReadError::InconsistentGraphData,
578c049f0408 rust-index: using `hg::index::Index` in `hg-cpython::dagops`
Georges Racinet <georges.racinet@octobus.net>
parents: 50979
diff changeset
    56
    ))
48854
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    57
}
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    58
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    59
/// Create the module, with `__package__` given from parent
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    60
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    61
    let dotted_name = &format!("{}.dagop", package);
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    62
    let m = PyModule::new(py, dotted_name)?;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    63
    m.add(py, "__package__", package)?;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    64
    m.add(py, "__doc__", "DAG operations - Rust implementation")?;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    65
    m.add(
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    66
        py,
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    67
        "headrevs",
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    68
        py_fn!(py, headrevs(index: PyObject, revs: PyObject)),
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    69
    )?;
48854
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    70
    m.add(
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    71
        py,
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    72
        "rank",
50979
4c5f6e95df84 rust: make `Revision` a newtype
Raphaël Gomès <rgomes@octobus.net>
parents: 48854
diff changeset
    73
        py_fn!(py, rank(index: PyObject, p1r: PyRevision, p2r: PyRevision)),
48854
8b8054b8e5a7 rust: expose rank computation function to python
pacien <pacien.trangirard@pacien.net>
parents: 43945
diff changeset
    74
    )?;
41694
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    75
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    76
    let sys = PyModule::import(py, "sys")?;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    77
    let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    78
    sys_modules.set_item(py, dotted_name, &m)?;
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    79
    // Example C code (see pyexpat.c and import.c) will "give away the
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    80
    // reference", but we won't because it will be consumed once the
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    81
    // Rust PyObject is dropped.
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    82
    Ok(m)
0c7b353ce100 rust-cpython: binding for headrevs()
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
    83
}