# HG changeset patch # User Raphaël Gomès <rgo...@octobus.net> # Date 1555442867 -7200 # Tue Apr 16 21:27:47 2019 +0200 # Node ID 8586c2273ee3f0c4dd08bf69474fa33bb19de405 # Parent ad489b44ef16ae39eb2c692fc5ccf3268a140a65 # EXP-Topic rust-filepatterns rust-filepatterns: add `rust-cpython` bindings for `filepatterns`
This change adds the `rust-cpython` interface for top-level functions and exceptions in the filepatterns module. Contrary to the Python implementation, this tries to have finer-grained exceptions to allow for better readability and flow control down the line. diff -r ad489b44ef16 -r 8586c2273ee3 rust/hg-cpython/src/exceptions.rs --- a/rust/hg-cpython/src/exceptions.rs Tue Apr 16 21:17:51 2019 +0200 +++ b/rust/hg-cpython/src/exceptions.rs Tue Apr 16 21:27:47 2019 +0200 @@ -12,8 +12,8 @@ //! existing Python exceptions if appropriate. //! //! [`GraphError`]: struct.GraphError.html -use cpython::exc::ValueError; -use cpython::{PyErr, Python}; +use cpython::exc::{ValueError, RuntimeError}; +use cpython::{PyErr, Python, exc}; use hg; py_exception!(rustext, GraphError, ValueError); @@ -28,9 +28,43 @@ match py .import("mercurial.error") .and_then(|m| m.get(py, "WdirUnsupported")) - { - Err(e) => e, - Ok(cls) => PyErr::from_instance(py, cls), + { + Err(e) => e, + Ok(cls) => PyErr::from_instance(py, cls), + } + } + } + } +} + +py_exception!(rustext, PatternError, RuntimeError); +py_exception!(rustext, PatternFileError, RuntimeError); + +impl PatternError { + pub fn pynew(py: Python, inner: hg::PatternError) -> PyErr { + match inner { + hg::PatternError::UnsupportedSyntax(m) => { + PatternError::new(py, ("PatternError", m)) + } + } + } +} + + +impl PatternFileError { + pub fn pynew(py: Python, inner: hg::PatternFileError) -> PyErr { + match inner { + hg::PatternFileError::IO(e) => { + let value = ( + e.raw_os_error().unwrap_or(2), + e.to_string() + ); + PyErr::new::<exc::IOError, _>(py, value) + } + hg::PatternFileError::Pattern(e, l) => { + match e { + hg::PatternError::UnsupportedSyntax(m) => + PatternFileError::new(py, ("PatternFileError", m, l)) } } } diff -r ad489b44ef16 -r 8586c2273ee3 rust/hg-cpython/src/filepatterns.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hg-cpython/src/filepatterns.rs Tue Apr 16 21:27:47 2019 +0200 @@ -0,0 +1,115 @@ +// filepatterns.rs +// +// Copyright 2019, Georges Racinet <graci...@anybox.fr>, +// Raphaël Gomès <rgo...@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 cpython::{ + exc, PyDict, PyErr, PyModule, PyResult, PyString, PyTuple, Python, + ToPyObject, +}; +use hg::{build_single_regex, read_pattern_file, PatternTuple}; +use exceptions::{ + PatternError, + PatternFileError, +}; + +/// 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: String, + warn: bool, + source_info: bool, +) -> PyResult<PyTuple> { + match read_pattern_file(file_path, warn) { + Ok((patterns, warnings)) => { + if source_info { + return Ok((patterns, warnings).to_py_object(py)); + } + let itemgetter = |x: &PatternTuple| x.0.to_py_object(py); + let results: Vec<PyString> = + patterns.iter().map(itemgetter).collect(); + Ok((results, warnings).to_py_object(py)) + } + Err(e) => Err(PatternFileError::pynew(py, e)), + } +} + +fn build_single_regex_wrapper( + py: Python, + kind: String, + pat: String, + globsuffix: String, +) -> PyResult<PyString> { + match build_single_regex( + kind.as_ref(), + pat.as_bytes(), + globsuffix.as_bytes(), + ) { + Ok(regex) => match String::from_utf8(regex) { + Ok(regex) => Ok(regex.to_py_object(py)), + Err(e) => Err(PyErr::new::<exc::UnicodeDecodeError, _>( + py, + e.to_string(), + )), + }, + 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: String, + pat: String, + globsuffix: String + ) + ), + )?; + m.add( + py, + "read_pattern_file", + py_fn!( + py, + read_pattern_file_wrapper( + file_path: String, + warn: bool, + source_info: bool + ) + ), + )?; + + 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) +} diff -r ad489b44ef16 -r 8586c2273ee3 rust/hg-cpython/src/lib.rs --- a/rust/hg-cpython/src/lib.rs Tue Apr 16 21:17:51 2019 +0200 +++ b/rust/hg-cpython/src/lib.rs Tue Apr 16 21:27:47 2019 +0200 @@ -29,6 +29,7 @@ mod conversion; pub mod dagops; pub mod exceptions; +pub mod filepatterns; py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| { m.add( @@ -40,6 +41,9 @@ let dotted_name: String = m.get(py, "__name__")?.extract(py)?; m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?; m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?; + m.add(py, "filepatterns", filepatterns::init_module(py, &dotted_name)?)?; m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?; + m.add(py, "PatternFileError", py.get_type::<exceptions::PatternFileError>())?; + m.add(py, "PatternError", py.get_type::<exceptions::PatternError>())?; Ok(()) }); _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel