rust/hg-cpython/src/filepatterns.rs
changeset 44137 3bd77c64bc74
parent 44136 baa4e7fdfd47
child 44138 5f841daf3b41
equal deleted inserted replaced
44136:baa4e7fdfd47 44137:3bd77c64bc74
     1 // filepatterns.rs
       
     2 //
       
     3 // Copyright 2019, Georges Racinet <gracinet@anybox.fr>,
       
     4 // Raphaël Gomès <rgomes@octobus.net>
       
     5 //
       
     6 // This software may be used and distributed according to the terms of the
       
     7 // GNU General Public License version 2 or any later version.
       
     8 
       
     9 //! Bindings for the `hg::filepatterns` module provided by the
       
    10 //! `hg-core` crate. From Python, this will be seen as `rustext.filepatterns`
       
    11 //! and can be used as replacement for the the pure `filepatterns` Python
       
    12 //! module.
       
    13 use crate::exceptions::{PatternError, PatternFileError};
       
    14 use cpython::{
       
    15     PyBytes, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, ToPyObject,
       
    16 };
       
    17 use hg::utils::files;
       
    18 use hg::{build_single_regex, read_pattern_file, LineNumber, PatternTuple};
       
    19 use std::path::PathBuf;
       
    20 
       
    21 /// Rust does not like functions with different return signatures.
       
    22 /// The 3-tuple version is always returned by the hg-core function,
       
    23 /// the (potential) conversion is handled at this level since it is not likely
       
    24 /// to have any measurable impact on performance.
       
    25 ///
       
    26 /// The Python implementation passes a function reference for `warn` instead
       
    27 /// of a boolean that is used to emit warnings while parsing. The Rust
       
    28 /// implementation chooses to accumulate the warnings and propagate them to
       
    29 /// Python upon completion. See the `readpatternfile` function in `match.py`
       
    30 /// for more details.
       
    31 fn read_pattern_file_wrapper(
       
    32     py: Python,
       
    33     file_path: PyObject,
       
    34     warn: bool,
       
    35     source_info: bool,
       
    36 ) -> PyResult<PyTuple> {
       
    37     let bytes = file_path.extract::<PyBytes>(py)?;
       
    38     let path = files::get_path_from_bytes(bytes.data(py));
       
    39     match read_pattern_file(path, warn) {
       
    40         Ok((patterns, warnings)) => {
       
    41             if source_info {
       
    42                 let itemgetter = |x: &PatternTuple| {
       
    43                     (PyBytes::new(py, &x.0), x.1, PyBytes::new(py, &x.2))
       
    44                 };
       
    45                 let results: Vec<(PyBytes, LineNumber, PyBytes)> =
       
    46                     patterns.iter().map(itemgetter).collect();
       
    47                 return Ok((results, warnings_to_py_bytes(py, &warnings))
       
    48                     .to_py_object(py));
       
    49             }
       
    50             let itemgetter = |x: &PatternTuple| PyBytes::new(py, &x.0);
       
    51             let results: Vec<PyBytes> =
       
    52                 patterns.iter().map(itemgetter).collect();
       
    53             Ok(
       
    54                 (results, warnings_to_py_bytes(py, &warnings))
       
    55                     .to_py_object(py),
       
    56             )
       
    57         }
       
    58         Err(e) => Err(PatternFileError::pynew(py, e)),
       
    59     }
       
    60 }
       
    61 
       
    62 fn warnings_to_py_bytes(
       
    63     py: Python,
       
    64     warnings: &[(PathBuf, Vec<u8>)],
       
    65 ) -> Vec<(PyBytes, PyBytes)> {
       
    66     warnings
       
    67         .iter()
       
    68         .map(|(path, syn)| {
       
    69             (
       
    70                 PyBytes::new(py, &files::get_bytes_from_path(path)),
       
    71                 PyBytes::new(py, syn),
       
    72             )
       
    73         })
       
    74         .collect()
       
    75 }
       
    76 
       
    77 fn build_single_regex_wrapper(
       
    78     py: Python,
       
    79     kind: PyObject,
       
    80     pat: PyObject,
       
    81     globsuffix: PyObject,
       
    82 ) -> PyResult<PyBytes> {
       
    83     match build_single_regex(
       
    84         kind.extract::<PyBytes>(py)?.data(py),
       
    85         pat.extract::<PyBytes>(py)?.data(py),
       
    86         globsuffix.extract::<PyBytes>(py)?.data(py),
       
    87     ) {
       
    88         Ok(regex) => Ok(PyBytes::new(py, &regex)),
       
    89         Err(e) => Err(PatternError::pynew(py, e)),
       
    90     }
       
    91 }
       
    92 
       
    93 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
       
    94     let dotted_name = &format!("{}.filepatterns", package);
       
    95     let m = PyModule::new(py, dotted_name)?;
       
    96 
       
    97     m.add(py, "__package__", package)?;
       
    98     m.add(
       
    99         py,
       
   100         "__doc__",
       
   101         "Patterns files parsing - Rust implementation",
       
   102     )?;
       
   103     m.add(
       
   104         py,
       
   105         "build_single_regex",
       
   106         py_fn!(
       
   107             py,
       
   108             build_single_regex_wrapper(
       
   109                 kind: PyObject,
       
   110                 pat: PyObject,
       
   111                 globsuffix: PyObject
       
   112             )
       
   113         ),
       
   114     )?;
       
   115     m.add(
       
   116         py,
       
   117         "read_pattern_file",
       
   118         py_fn!(
       
   119             py,
       
   120             read_pattern_file_wrapper(
       
   121                 file_path: PyObject,
       
   122                 warn: bool,
       
   123                 source_info: bool
       
   124             )
       
   125         ),
       
   126     )?;
       
   127     m.add(py, "PatternError", py.get_type::<PatternError>())?;
       
   128     let sys = PyModule::import(py, "sys")?;
       
   129     let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
       
   130     sys_modules.set_item(py, dotted_name, &m)?;
       
   131 
       
   132     Ok(m)
       
   133 }