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