comparison rust/hg-cpython/src/filepatterns.rs @ 42350:94f3a73b6672

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. Differential Revision: https://phab.mercurial-scm.org/D6272
author Rapha?l Gom?s <rgomes@octobus.net>
date Fri, 17 May 2019 09:36:29 -0400
parents rust/hg-cpython/src/ancestors.rs@060c030c9993
children 9609430d3625
comparison
equal deleted inserted replaced
42349:e8f3740cc067 42350:94f3a73b6672
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 module.
12 //!
13 use cpython::{
14 exc, PyDict, PyErr, PyModule, PyResult, PyString, PyTuple, Python,
15 ToPyObject,
16 };
17 use hg::{build_single_regex, read_pattern_file, PatternTuple};
18 use exceptions::{
19 PatternError,
20 PatternFileError,
21 };
22
23 /// Rust does not like functions with different return signatures.
24 /// The 3-tuple version is always returned by the hg-core function,
25 /// the (potential) conversion is handled at this level since it is not likely
26 /// to have any measurable impact on performance.
27 ///
28 /// The Python implementation passes a function reference for `warn` instead
29 /// of a boolean that is used to emit warnings while parsing. The Rust
30 /// implementation chooses to accumulate the warnings and propagate them to
31 /// Python upon completion. See the `readpatternfile` function in `match.py`
32 /// for more details.
33 fn read_pattern_file_wrapper(
34 py: Python,
35 file_path: String,
36 warn: bool,
37 source_info: bool,
38 ) -> PyResult<PyTuple> {
39 match read_pattern_file(file_path, warn) {
40 Ok((patterns, warnings)) => {
41 if source_info {
42 return Ok((patterns, warnings).to_py_object(py));
43 }
44 let itemgetter = |x: &PatternTuple| x.0.to_py_object(py);
45 let results: Vec<PyString> =
46 patterns.iter().map(itemgetter).collect();
47 Ok((results, warnings).to_py_object(py))
48 }
49 Err(e) => Err(PatternFileError::pynew(py, e)),
50 }
51 }
52
53 fn build_single_regex_wrapper(
54 py: Python,
55 kind: String,
56 pat: String,
57 globsuffix: String,
58 ) -> PyResult<PyString> {
59 match build_single_regex(
60 kind.as_ref(),
61 pat.as_bytes(),
62 globsuffix.as_bytes(),
63 ) {
64 Ok(regex) => match String::from_utf8(regex) {
65 Ok(regex) => Ok(regex.to_py_object(py)),
66 Err(e) => Err(PyErr::new::<exc::UnicodeDecodeError, _>(
67 py,
68 e.to_string(),
69 )),
70 },
71 Err(e) => Err(PatternError::pynew(py, e)),
72 }
73 }
74
75 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
76 let dotted_name = &format!("{}.filepatterns", package);
77 let m = PyModule::new(py, dotted_name)?;
78
79 m.add(py, "__package__", package)?;
80 m.add(
81 py,
82 "__doc__",
83 "Patterns files parsing - Rust implementation",
84 )?;
85 m.add(
86 py,
87 "build_single_regex",
88 py_fn!(
89 py,
90 build_single_regex_wrapper(
91 kind: String,
92 pat: String,
93 globsuffix: String
94 )
95 ),
96 )?;
97 m.add(
98 py,
99 "read_pattern_file",
100 py_fn!(
101 py,
102 read_pattern_file_wrapper(
103 file_path: String,
104 warn: bool,
105 source_info: bool
106 )
107 ),
108 )?;
109
110 let sys = PyModule::import(py, "sys")?;
111 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
112 sys_modules.set_item(py, dotted_name, &m)?;
113
114 Ok(m)
115 }