view rust/hg-cpython/src/copy_tracing.rs @ 46587:cb4b0b0c6de4

copies-rust: split up combine_changeset_copies function into a struct … such that each iteration of its former loop is now a method call, with the caller driving the loop. This entirely removes the need for the `DataHolder` hack: the method now takes a `ChangedFiles<'_>` parameter that borrows a bytes buffer that can be owned by the caller’s stack frame, just for the duration of that call. Differential Revision: https://phab.mercurial-scm.org/D9683
author Simon Sapin <simon.sapin@octobus.net>
date Tue, 05 Jan 2021 21:02:00 +0100
parents 34827c95092c
children 47557ea79fc7
line wrap: on
line source

use cpython::ObjectProtocol;
use cpython::PyBytes;
use cpython::PyDict;
use cpython::PyList;
use cpython::PyModule;
use cpython::PyObject;
use cpython::PyResult;
use cpython::PyTuple;
use cpython::Python;

use hg::copy_tracing::ChangedFiles;
use hg::copy_tracing::CombineChangesetCopies;
use hg::Revision;

/// Combines copies information contained into revision `revs` to build a copy
/// map.
///
/// See mercurial/copies.py for details
pub fn combine_changeset_copies_wrapper(
    py: Python,
    revs: PyList,
    children_count: PyDict,
    target_rev: Revision,
    rev_info: PyObject,
) -> PyResult<PyDict> {
    let children_count = children_count
        .items(py)
        .iter()
        .map(|(k, v)| Ok((k.extract(py)?, v.extract(py)?)))
        .collect::<PyResult<_>>()?;

    /// (Revision number, parent 1, parent 2, copy data for this revision)
    type RevInfo = (Revision, Revision, Revision, Option<PyBytes>);

    let revs_info = revs.iter(py).map(|rev_py| -> PyResult<RevInfo> {
        let rev = rev_py.extract(py)?;
        let tuple: PyTuple =
            rev_info.call(py, (rev_py,), None)?.cast_into(py)?;
        let p1 = tuple.get_item(py, 0).extract(py)?;
        let p2 = tuple.get_item(py, 1).extract(py)?;
        let opt_bytes = tuple.get_item(py, 2).extract(py)?;
        Ok((rev, p1, p2, opt_bytes))
    });

    let mut combine_changeset_copies =
        CombineChangesetCopies::new(children_count);

    for rev_info in revs_info {
        let (rev, p1, p2, opt_bytes) = rev_info?;
        let files = match &opt_bytes {
            Some(bytes) => ChangedFiles::new(bytes.data(py)),
            // value was presumably None, meaning they was no copy data.
            None => ChangedFiles::new_empty(),
        };

        combine_changeset_copies.add_revision(rev, p1, p2, files)
    }
    let path_copies = combine_changeset_copies.finish(target_rev);
    let out = PyDict::new(py);
    for (dest, source) in path_copies.into_iter() {
        out.set_item(
            py,
            PyBytes::new(py, &dest.into_vec()),
            PyBytes::new(py, &source.into_vec()),
        )?;
    }
    Ok(out)
}

/// Create the module, with `__package__` given from parent
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
    let dotted_name = &format!("{}.copy_tracing", package);
    let m = PyModule::new(py, dotted_name)?;

    m.add(py, "__package__", package)?;
    m.add(py, "__doc__", "Copy tracing - Rust implementation")?;

    m.add(
        py,
        "combine_changeset_copies",
        py_fn!(
            py,
            combine_changeset_copies_wrapper(
                revs: PyList,
                children: PyDict,
                target_rev: Revision,
                rev_info: PyObject
            )
        ),
    )?;

    let sys = PyModule::import(py, "sys")?;
    let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
    sys_modules.set_item(py, dotted_name, &m)?;

    Ok(m)
}