##// END OF EJS Templates
rust-cpython: do not convert warning pattern to utf-8 bytes...
Yuya Nishihara -
r44161:3078f9a4 default draft
parent child Browse files
Show More
@@ -1,128 +1,136 b''
1 1 // files.rs
2 2 //
3 3 // Copyright 2019
4 4 // Raphaël Gomès <rgomes@octobus.net>,
5 5 // Yuya Nishihara <yuya@tcha.org>
6 6 //
7 7 // This software may be used and distributed according to the terms of the
8 8 // GNU General Public License version 2 or any later version.
9 9
10 10 //! Functions for fiddling with files.
11 11
12 12 use crate::utils::hg_path::{HgPath, HgPathBuf};
13 13 use std::iter::FusedIterator;
14 14
15 15 use std::fs::Metadata;
16 16 use std::path::Path;
17 17
18 18 pub fn get_path_from_bytes(bytes: &[u8]) -> &Path {
19 19 let os_str;
20 20 #[cfg(unix)]
21 21 {
22 22 use std::os::unix::ffi::OsStrExt;
23 23 os_str = std::ffi::OsStr::from_bytes(bytes);
24 24 }
25 25 // TODO Handle other platforms
26 26 // TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
27 27 // Perhaps, the return type would have to be Result<PathBuf>.
28 28
29 29 Path::new(os_str)
30 30 }
31 31
32 // TODO: need to convert from WTF8 to MBCS bytes on Windows.
33 // that's why Vec<u8> is returned.
34 #[cfg(unix)]
35 pub fn get_bytes_from_path(path: impl AsRef<Path>) -> Vec<u8> {
36 use std::os::unix::ffi::OsStrExt;
37 path.as_ref().as_os_str().as_bytes().to_vec()
38 }
39
32 40 /// An iterator over repository path yielding itself and its ancestors.
33 41 #[derive(Copy, Clone, Debug)]
34 42 pub struct Ancestors<'a> {
35 43 next: Option<&'a HgPath>,
36 44 }
37 45
38 46 impl<'a> Iterator for Ancestors<'a> {
39 47 type Item = &'a HgPath;
40 48
41 49 fn next(&mut self) -> Option<Self::Item> {
42 50 let next = self.next;
43 51 self.next = match self.next {
44 52 Some(s) if s.is_empty() => None,
45 53 Some(s) => {
46 54 let p = s.bytes().rposition(|c| *c == b'/').unwrap_or(0);
47 55 Some(HgPath::new(&s.as_bytes()[..p]))
48 56 }
49 57 None => None,
50 58 };
51 59 next
52 60 }
53 61 }
54 62
55 63 impl<'a> FusedIterator for Ancestors<'a> {}
56 64
57 65 /// Returns an iterator yielding ancestor directories of the given repository
58 66 /// path.
59 67 ///
60 68 /// The path is separated by '/', and must not start with '/'.
61 69 ///
62 70 /// The path itself isn't included unless it is b"" (meaning the root
63 71 /// directory.)
64 72 pub fn find_dirs<'a>(path: &'a HgPath) -> Ancestors<'a> {
65 73 let mut dirs = Ancestors { next: Some(path) };
66 74 if !path.is_empty() {
67 75 dirs.next(); // skip itself
68 76 }
69 77 dirs
70 78 }
71 79
72 80 /// TODO more than ASCII?
73 81 pub fn normalize_case(path: &HgPath) -> HgPathBuf {
74 82 #[cfg(windows)] // NTFS compares via upper()
75 83 return path.to_ascii_uppercase();
76 84 #[cfg(unix)]
77 85 path.to_ascii_lowercase()
78 86 }
79 87
80 88 #[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
81 89 pub struct HgMetadata {
82 90 pub st_dev: u64,
83 91 pub st_mode: u32,
84 92 pub st_nlink: u64,
85 93 pub st_size: u64,
86 94 pub st_mtime: i64,
87 95 pub st_ctime: i64,
88 96 }
89 97
90 98 // TODO support other plaforms
91 99 #[cfg(unix)]
92 100 impl HgMetadata {
93 101 pub fn from_metadata(metadata: Metadata) -> Self {
94 102 use std::os::unix::fs::MetadataExt;
95 103 Self {
96 104 st_dev: metadata.dev(),
97 105 st_mode: metadata.mode(),
98 106 st_nlink: metadata.nlink(),
99 107 st_size: metadata.size(),
100 108 st_mtime: metadata.mtime(),
101 109 st_ctime: metadata.ctime(),
102 110 }
103 111 }
104 112 }
105 113
106 114 #[cfg(test)]
107 115 mod tests {
108 116 use super::*;
109 117
110 118 #[test]
111 119 fn find_dirs_some() {
112 120 let mut dirs = super::find_dirs(HgPath::new(b"foo/bar/baz"));
113 121 assert_eq!(dirs.next(), Some(HgPath::new(b"foo/bar")));
114 122 assert_eq!(dirs.next(), Some(HgPath::new(b"foo")));
115 123 assert_eq!(dirs.next(), Some(HgPath::new(b"")));
116 124 assert_eq!(dirs.next(), None);
117 125 assert_eq!(dirs.next(), None);
118 126 }
119 127
120 128 #[test]
121 129 fn find_dirs_empty() {
122 130 // looks weird, but mercurial.pathutil.finddirs(b"") yields b""
123 131 let mut dirs = super::find_dirs(HgPath::new(b""));
124 132 assert_eq!(dirs.next(), Some(HgPath::new(b"")));
125 133 assert_eq!(dirs.next(), None);
126 134 assert_eq!(dirs.next(), None);
127 135 }
128 136 }
@@ -1,134 +1,134 b''
1 1 // filepatterns.rs
2 2 //
3 3 // Copyright 2019, Georges Racinet <gracinet@anybox.fr>,
4 4 // Raphaël Gomès <rgomes@octobus.net>
5 5 //
6 6 // This software may be used and distributed according to the terms of the
7 7 // GNU General Public License version 2 or any later version.
8 8
9 9 //! Bindings for the `hg::filepatterns` module provided by the
10 10 //! `hg-core` crate. From Python, this will be seen as `rustext.filepatterns`
11 11 //! and can be used as replacement for the the pure `filepatterns` Python
12 12 //! module.
13 13 //!
14 14 use crate::exceptions::{PatternError, PatternFileError};
15 15 use cpython::{
16 16 PyBytes, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, ToPyObject,
17 17 };
18 18 use hg::utils::files;
19 19 use hg::{build_single_regex, read_pattern_file, LineNumber, PatternTuple};
20 20 use std::path::PathBuf;
21 21
22 22 /// Rust does not like functions with different return signatures.
23 23 /// The 3-tuple version is always returned by the hg-core function,
24 24 /// the (potential) conversion is handled at this level since it is not likely
25 25 /// to have any measurable impact on performance.
26 26 ///
27 27 /// The Python implementation passes a function reference for `warn` instead
28 28 /// of a boolean that is used to emit warnings while parsing. The Rust
29 29 /// implementation chooses to accumulate the warnings and propagate them to
30 30 /// Python upon completion. See the `readpatternfile` function in `match.py`
31 31 /// for more details.
32 32 fn read_pattern_file_wrapper(
33 33 py: Python,
34 34 file_path: PyObject,
35 35 warn: bool,
36 36 source_info: bool,
37 37 ) -> PyResult<PyTuple> {
38 38 let bytes = file_path.extract::<PyBytes>(py)?;
39 39 let path = files::get_path_from_bytes(bytes.data(py));
40 40 match read_pattern_file(path, warn) {
41 41 Ok((patterns, warnings)) => {
42 42 if source_info {
43 43 let itemgetter = |x: &PatternTuple| {
44 44 (PyBytes::new(py, &x.0), x.1, PyBytes::new(py, &x.2))
45 45 };
46 46 let results: Vec<(PyBytes, LineNumber, PyBytes)> =
47 47 patterns.iter().map(itemgetter).collect();
48 48 return Ok((results, warnings_to_py_bytes(py, &warnings))
49 49 .to_py_object(py));
50 50 }
51 51 let itemgetter = |x: &PatternTuple| PyBytes::new(py, &x.0);
52 52 let results: Vec<PyBytes> =
53 53 patterns.iter().map(itemgetter).collect();
54 54 Ok(
55 55 (results, warnings_to_py_bytes(py, &warnings))
56 56 .to_py_object(py),
57 57 )
58 58 }
59 59 Err(e) => Err(PatternFileError::pynew(py, e)),
60 60 }
61 61 }
62 62
63 63 fn warnings_to_py_bytes(
64 64 py: Python,
65 65 warnings: &[(PathBuf, Vec<u8>)],
66 66 ) -> Vec<(PyBytes, PyBytes)> {
67 67 warnings
68 68 .iter()
69 69 .map(|(path, syn)| {
70 70 (
71 PyBytes::new(py, &path.to_string_lossy().as_bytes()),
71 PyBytes::new(py, &files::get_bytes_from_path(path)),
72 72 PyBytes::new(py, syn),
73 73 )
74 74 })
75 75 .collect()
76 76 }
77 77
78 78 fn build_single_regex_wrapper(
79 79 py: Python,
80 80 kind: PyObject,
81 81 pat: PyObject,
82 82 globsuffix: PyObject,
83 83 ) -> PyResult<PyBytes> {
84 84 match build_single_regex(
85 85 kind.extract::<PyBytes>(py)?.data(py),
86 86 pat.extract::<PyBytes>(py)?.data(py),
87 87 globsuffix.extract::<PyBytes>(py)?.data(py),
88 88 ) {
89 89 Ok(regex) => Ok(PyBytes::new(py, &regex)),
90 90 Err(e) => Err(PatternError::pynew(py, e)),
91 91 }
92 92 }
93 93
94 94 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
95 95 let dotted_name = &format!("{}.filepatterns", package);
96 96 let m = PyModule::new(py, dotted_name)?;
97 97
98 98 m.add(py, "__package__", package)?;
99 99 m.add(
100 100 py,
101 101 "__doc__",
102 102 "Patterns files parsing - Rust implementation",
103 103 )?;
104 104 m.add(
105 105 py,
106 106 "build_single_regex",
107 107 py_fn!(
108 108 py,
109 109 build_single_regex_wrapper(
110 110 kind: PyObject,
111 111 pat: PyObject,
112 112 globsuffix: PyObject
113 113 )
114 114 ),
115 115 )?;
116 116 m.add(
117 117 py,
118 118 "read_pattern_file",
119 119 py_fn!(
120 120 py,
121 121 read_pattern_file_wrapper(
122 122 file_path: PyObject,
123 123 warn: bool,
124 124 source_info: bool
125 125 )
126 126 ),
127 127 )?;
128 128 m.add(py, "PatternError", py.get_type::<PatternError>())?;
129 129 let sys = PyModule::import(py, "sys")?;
130 130 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
131 131 sys_modules.set_item(py, dotted_name, &m)?;
132 132
133 133 Ok(m)
134 134 }
General Comments 0
You need to be logged in to leave comments. Login now