|
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 } |