view rust/hg-cpython/src/filepatterns.rs @ 43961:b69d5f3a41d0

rust-index: add a struct wrapping the C index Implementing the full index logic in one go is journey larger than we would like. To achieve a smoother transition, we start with a simple Rust wrapper that delegates allwork to the current C implementation. Once we will have a fully working index object in Rust, we can easily start using more and more Rust Code with it. The object in this patch is functional and tested. However, multiple of the currently existing rust (in the `hg-cpython` crate) requires a `Graph`. Right now we build this `Graph` (as cindex::Index) using the C index passed as a PyObject. They will have to be updated to be made compatible. Differential Revision: https://phab.mercurial-scm.org/D7655
author Georges Racinet <georges.racinet@octobus.net>
date Mon, 23 Dec 2019 10:02:50 -0800
parents ce088b38f92b
children
line wrap: on
line source

// filepatterns.rs
//
// Copyright 2019, Georges Racinet <gracinet@anybox.fr>,
// Raphaël Gomès <rgomes@octobus.net>
//
// 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::filepatterns` module provided by the
//! `hg-core` crate. From Python, this will be seen as `rustext.filepatterns`
//! and can be used as replacement for the the pure `filepatterns` Python
//! module.
use crate::exceptions::{PatternError, PatternFileError};
use cpython::{
    PyBytes, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, ToPyObject,
};
use hg::utils::files;
use hg::{build_single_regex, read_pattern_file, LineNumber, PatternTuple};
use std::path::PathBuf;

/// Rust does not like functions with different return signatures.
/// The 3-tuple version is always returned by the hg-core function,
/// the (potential) conversion is handled at this level since it is not likely
/// to have any measurable impact on performance.
///
/// The Python implementation passes a function reference for `warn` instead
/// of a boolean that is used to emit warnings while parsing. The Rust
/// implementation chooses to accumulate the warnings and propagate them to
/// Python upon completion. See the `readpatternfile` function in `match.py`
/// for more details.
fn read_pattern_file_wrapper(
    py: Python,
    file_path: PyObject,
    warn: bool,
    source_info: bool,
) -> PyResult<PyTuple> {
    let bytes = file_path.extract::<PyBytes>(py)?;
    let path = files::get_path_from_bytes(bytes.data(py));
    match read_pattern_file(path, warn) {
        Ok((patterns, warnings)) => {
            if source_info {
                let itemgetter = |x: &PatternTuple| {
                    (PyBytes::new(py, &x.0), x.1, PyBytes::new(py, &x.2))
                };
                let results: Vec<(PyBytes, LineNumber, PyBytes)> =
                    patterns.iter().map(itemgetter).collect();
                return Ok((results, warnings_to_py_bytes(py, &warnings))
                    .to_py_object(py));
            }
            let itemgetter = |x: &PatternTuple| PyBytes::new(py, &x.0);
            let results: Vec<PyBytes> =
                patterns.iter().map(itemgetter).collect();
            Ok(
                (results, warnings_to_py_bytes(py, &warnings))
                    .to_py_object(py),
            )
        }
        Err(e) => Err(PatternFileError::pynew(py, e)),
    }
}

fn warnings_to_py_bytes(
    py: Python,
    warnings: &[(PathBuf, Vec<u8>)],
) -> Vec<(PyBytes, PyBytes)> {
    warnings
        .iter()
        .map(|(path, syn)| {
            (
                PyBytes::new(py, &files::get_bytes_from_path(path)),
                PyBytes::new(py, syn),
            )
        })
        .collect()
}

fn build_single_regex_wrapper(
    py: Python,
    kind: PyObject,
    pat: PyObject,
    globsuffix: PyObject,
) -> PyResult<PyBytes> {
    match build_single_regex(
        kind.extract::<PyBytes>(py)?.data(py),
        pat.extract::<PyBytes>(py)?.data(py),
        globsuffix.extract::<PyBytes>(py)?.data(py),
    ) {
        Ok(regex) => Ok(PyBytes::new(py, &regex)),
        Err(e) => Err(PatternError::pynew(py, e)),
    }
}

pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
    let dotted_name = &format!("{}.filepatterns", package);
    let m = PyModule::new(py, dotted_name)?;

    m.add(py, "__package__", package)?;
    m.add(
        py,
        "__doc__",
        "Patterns files parsing - Rust implementation",
    )?;
    m.add(
        py,
        "build_single_regex",
        py_fn!(
            py,
            build_single_regex_wrapper(
                kind: PyObject,
                pat: PyObject,
                globsuffix: PyObject
            )
        ),
    )?;
    m.add(
        py,
        "read_pattern_file",
        py_fn!(
            py,
            read_pattern_file_wrapper(
                file_path: PyObject,
                warn: bool,
                source_info: bool
            )
        ),
    )?;
    m.add(py, "PatternError", py.get_type::<PatternError>())?;
    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)
}