##// END OF EJS Templates
rust-status: update rust-cpython bridge to account for the changes in core...
Raphaël Gomès -
r45016:f96b28aa default
parent child Browse files
Show More
@@ -48,6 +48,20 b' pub enum BadType {'
48 Unknown,
48 Unknown,
49 }
49 }
50
50
51 impl ToString for BadType {
52 fn to_string(&self) -> String {
53 match self {
54 BadType::CharacterDevice => "character device",
55 BadType::BlockDevice => "block device",
56 BadType::FIFO => "fifo",
57 BadType::Socket => "socket",
58 BadType::Directory => "directory",
59 BadType::Unknown => "unknown",
60 }
61 .to_string()
62 }
63 }
64
51 /// Was explicitly matched but cannot be found/accessed
65 /// Was explicitly matched but cannot be found/accessed
52 #[derive(Debug)]
66 #[derive(Debug)]
53 pub enum BadMatch {
67 pub enum BadMatch {
@@ -14,12 +14,15 b' mod dirs_multiset;'
14 mod dirstate_map;
14 mod dirstate_map;
15 mod non_normal_entries;
15 mod non_normal_entries;
16 mod status;
16 mod status;
17 use crate::dirstate::{
17 use crate::{
18 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
18 dirstate::{
19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
20 },
21 exceptions,
19 };
22 };
20 use cpython::{
23 use cpython::{
21 exc, PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence,
24 exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
22 Python,
25 PySequence, Python,
23 };
26 };
24 use hg::{
27 use hg::{
25 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParseError, EntryState,
28 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParseError, EntryState,
@@ -107,6 +110,11 b' pub fn init_module(py: Python, package: '
107 m.add(py, "__package__", package)?;
110 m.add(py, "__package__", package)?;
108 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
111 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
109
112
113 m.add(
114 py,
115 "FallbackError",
116 py.get_type::<exceptions::FallbackError>(),
117 )?;
110 m.add_class::<Dirs>(py)?;
118 m.add_class::<Dirs>(py)?;
111 m.add_class::<DirstateMap>(py)?;
119 m.add_class::<DirstateMap>(py)?;
112 m.add(
120 m.add(
@@ -118,9 +126,12 b' pub fn init_module(py: Python, package: '
118 dmap: DirstateMap,
126 dmap: DirstateMap,
119 root_dir: PyObject,
127 root_dir: PyObject,
120 matcher: PyObject,
128 matcher: PyObject,
121 list_clean: bool,
129 ignorefiles: PyList,
130 check_exec: bool,
122 last_normal_time: i64,
131 last_normal_time: i64,
123 check_exec: bool
132 list_clean: bool,
133 list_ignored: bool,
134 list_unknown: bool
124 )
135 )
125 ),
136 ),
126 )?;
137 )?;
@@ -9,33 +9,34 b''
9 //! `hg-core` crate. From Python, this will be seen as
9 //! `hg-core` crate. From Python, this will be seen as
10 //! `rustext.dirstate.status`.
10 //! `rustext.dirstate.status`.
11
11
12 use crate::dirstate::DirstateMap;
12 use crate::{dirstate::DirstateMap, exceptions::FallbackError};
13 use cpython::exc::ValueError;
14 use cpython::{
13 use cpython::{
15 ObjectProtocol, PyBytes, PyErr, PyList, PyObject, PyResult, PyTuple,
14 exc::ValueError, ObjectProtocol, PyBytes, PyErr, PyList, PyObject,
16 Python, PythonObject, ToPyObject,
15 PyResult, PyTuple, Python, PythonObject, ToPyObject,
17 };
16 };
18 use hg::utils::hg_path::HgPathBuf;
19 use hg::{
17 use hg::{
20 matchers::{AlwaysMatcher, FileMatcher},
18 matchers::{AlwaysMatcher, FileMatcher, IncludeMatcher},
21 status,
19 parse_pattern_syntax, status,
22 utils::{files::get_path_from_bytes, hg_path::HgPath},
20 utils::{
23 DirstateStatus,
21 files::{get_bytes_from_path, get_path_from_bytes},
22 hg_path::{HgPath, HgPathBuf},
23 },
24 BadMatch, DirstateStatus, IgnorePattern, PatternFileWarning, StatusError,
25 StatusOptions,
24 };
26 };
25 use std::borrow::Borrow;
27 use std::borrow::{Borrow, Cow};
26
28
27 /// This will be useless once trait impls for collection are added to `PyBytes`
29 /// This will be useless once trait impls for collection are added to `PyBytes`
28 /// upstream.
30 /// upstream.
29 fn collect_pybytes_list<P: AsRef<HgPath>>(
31 fn collect_pybytes_list(
30 py: Python,
32 py: Python,
31 collection: &[P],
33 collection: &[impl AsRef<HgPath>],
32 ) -> PyList {
34 ) -> PyList {
33 let list = PyList::new(py, &[]);
35 let list = PyList::new(py, &[]);
34
36
35 for (i, path) in collection.iter().enumerate() {
37 for path in collection.iter() {
36 list.insert(
38 list.append(
37 py,
39 py,
38 i,
39 PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
40 PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
40 )
41 )
41 }
42 }
@@ -43,34 +44,97 b' fn collect_pybytes_list<P: AsRef<HgPath>'
43 list
44 list
44 }
45 }
45
46
47 fn collect_bad_matches(
48 py: Python,
49 collection: &[(impl AsRef<HgPath>, BadMatch)],
50 ) -> PyResult<PyList> {
51 let list = PyList::new(py, &[]);
52
53 let os = py.import("os")?;
54 let get_error_message = |code: i32| -> PyResult<_> {
55 os.call(
56 py,
57 "strerror",
58 PyTuple::new(py, &[code.to_py_object(py).into_object()]),
59 None,
60 )
61 };
62
63 for (path, bad_match) in collection.iter() {
64 let message = match bad_match {
65 BadMatch::OsError(code) => get_error_message(*code)?,
66 BadMatch::BadType(bad_type) => format!(
67 "unsupported file type (type is {})",
68 bad_type.to_string()
69 )
70 .to_py_object(py)
71 .into_object(),
72 };
73 list.append(
74 py,
75 (PyBytes::new(py, path.as_ref().as_bytes()), message)
76 .to_py_object(py)
77 .into_object(),
78 )
79 }
80
81 Ok(list)
82 }
83
84 fn handle_fallback(py: Python, err: StatusError) -> PyErr {
85 match err {
86 StatusError::Pattern(e) => {
87 PyErr::new::<FallbackError, _>(py, e.to_string())
88 }
89 e => PyErr::new::<ValueError, _>(py, e.to_string()),
90 }
91 }
92
46 pub fn status_wrapper(
93 pub fn status_wrapper(
47 py: Python,
94 py: Python,
48 dmap: DirstateMap,
95 dmap: DirstateMap,
49 matcher: PyObject,
96 matcher: PyObject,
50 root_dir: PyObject,
97 root_dir: PyObject,
51 list_clean: bool,
98 ignore_files: PyList,
99 check_exec: bool,
52 last_normal_time: i64,
100 last_normal_time: i64,
53 check_exec: bool,
101 list_clean: bool,
54 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
102 list_ignored: bool,
103 list_unknown: bool,
104 ) -> PyResult<PyTuple> {
55 let bytes = root_dir.extract::<PyBytes>(py)?;
105 let bytes = root_dir.extract::<PyBytes>(py)?;
56 let root_dir = get_path_from_bytes(bytes.data(py));
106 let root_dir = get_path_from_bytes(bytes.data(py));
57
107
58 let dmap: DirstateMap = dmap.to_py_object(py);
108 let dmap: DirstateMap = dmap.to_py_object(py);
59 let dmap = dmap.get_inner(py);
109 let dmap = dmap.get_inner(py);
60
110
111 let ignore_files: PyResult<Vec<_>> = ignore_files
112 .iter(py)
113 .map(|b| {
114 let file = b.extract::<PyBytes>(py)?;
115 Ok(get_path_from_bytes(file.data(py)).to_owned())
116 })
117 .collect();
118 let ignore_files = ignore_files?;
119
61 match matcher.get_type(py).name(py).borrow() {
120 match matcher.get_type(py).name(py).borrow() {
62 "alwaysmatcher" => {
121 "alwaysmatcher" => {
63 let matcher = AlwaysMatcher;
122 let matcher = AlwaysMatcher;
64 let (lookup, status_res) = status(
123 let ((lookup, status_res), warnings) = status(
65 &dmap,
124 &dmap,
66 &matcher,
125 &matcher,
67 &root_dir,
126 &root_dir,
68 list_clean,
127 &ignore_files,
69 last_normal_time,
128 StatusOptions {
70 check_exec,
129 check_exec,
130 last_normal_time,
131 list_clean,
132 list_ignored,
133 list_unknown,
134 },
71 )
135 )
72 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
136 .map_err(|e| handle_fallback(py, e))?;
73 build_response(lookup, status_res, py)
137 build_response(py, lookup, status_res, warnings)
74 }
138 }
75 "exactmatcher" => {
139 "exactmatcher" => {
76 let files = matcher.call_method(
140 let files = matcher.call_method(
@@ -92,16 +156,78 b' pub fn status_wrapper('
92 let files = files?;
156 let files = files?;
93 let matcher = FileMatcher::new(&files)
157 let matcher = FileMatcher::new(&files)
94 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
158 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
95 let (lookup, status_res) = status(
159 let ((lookup, status_res), warnings) = status(
96 &dmap,
160 &dmap,
97 &matcher,
161 &matcher,
98 &root_dir,
162 &root_dir,
99 list_clean,
163 &ignore_files,
100 last_normal_time,
164 StatusOptions {
101 check_exec,
165 check_exec,
166 last_normal_time,
167 list_clean,
168 list_ignored,
169 list_unknown,
170 },
102 )
171 )
103 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
172 .map_err(|e| handle_fallback(py, e))?;
104 build_response(lookup, status_res, py)
173 build_response(py, lookup, status_res, warnings)
174 }
175 "includematcher" => {
176 // Get the patterns from Python even though most of them are
177 // redundant with those we will parse later on, as they include
178 // those passed from the command line.
179 let ignore_patterns: PyResult<Vec<_>> = matcher
180 .getattr(py, "_kindpats")?
181 .iter(py)?
182 .map(|k| {
183 let k = k?;
184 let syntax = parse_pattern_syntax(
185 &[
186 k.get_item(py, 0)?
187 .extract::<PyBytes>(py)?
188 .data(py),
189 &b":"[..],
190 ]
191 .concat(),
192 )
193 .map_err(|e| {
194 handle_fallback(py, StatusError::Pattern(e))
195 })?;
196 let pattern = k.get_item(py, 1)?.extract::<PyBytes>(py)?;
197 let pattern = pattern.data(py);
198 let source = k.get_item(py, 2)?.extract::<PyBytes>(py)?;
199 let source = get_path_from_bytes(source.data(py));
200 let new = IgnorePattern::new(syntax, pattern, source);
201 Ok(new)
202 })
203 .collect();
204
205 let ignore_patterns = ignore_patterns?;
206 let mut all_warnings = vec![];
207
208 let (matcher, warnings) =
209 IncludeMatcher::new(ignore_patterns, &root_dir)
210 .map_err(|e| handle_fallback(py, e.into()))?;
211 all_warnings.extend(warnings);
212
213 let ((lookup, status_res), warnings) = status(
214 &dmap,
215 &matcher,
216 &root_dir,
217 &ignore_files,
218 StatusOptions {
219 check_exec,
220 last_normal_time,
221 list_clean,
222 list_ignored,
223 list_unknown,
224 },
225 )
226 .map_err(|e| handle_fallback(py, e))?;
227
228 all_warnings.extend(warnings);
229
230 build_response(py, lookup, status_res, all_warnings)
105 }
231 }
106 e => {
232 e => {
107 return Err(PyErr::new::<ValueError, _>(
233 return Err(PyErr::new::<ValueError, _>(
@@ -113,17 +239,56 b' pub fn status_wrapper('
113 }
239 }
114
240
115 fn build_response(
241 fn build_response(
116 lookup: Vec<&HgPath>,
242 py: Python,
243 lookup: Vec<Cow<HgPath>>,
117 status_res: DirstateStatus,
244 status_res: DirstateStatus,
118 py: Python,
245 warnings: Vec<PatternFileWarning>,
119 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
246 ) -> PyResult<PyTuple> {
120 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
247 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
121 let added = collect_pybytes_list(py, status_res.added.as_ref());
248 let added = collect_pybytes_list(py, status_res.added.as_ref());
122 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
249 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
123 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
250 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
124 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
251 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
252 let ignored = collect_pybytes_list(py, status_res.ignored.as_ref());
253 let unknown = collect_pybytes_list(py, status_res.unknown.as_ref());
125 let lookup = collect_pybytes_list(py, lookup.as_ref());
254 let lookup = collect_pybytes_list(py, lookup.as_ref());
126 let unknown = PyList::new(py, &[]);
255 let bad = collect_bad_matches(py, status_res.bad.as_ref())?;
256 let py_warnings = PyList::new(py, &[]);
257 for warning in warnings.iter() {
258 // We use duck-typing on the Python side for dispatch, good enough for
259 // now.
260 match warning {
261 PatternFileWarning::InvalidSyntax(file, syn) => {
262 py_warnings.append(
263 py,
264 (
265 PyBytes::new(py, &get_bytes_from_path(&file)),
266 PyBytes::new(py, syn),
267 )
268 .to_py_object(py)
269 .into_object(),
270 );
271 }
272 PatternFileWarning::NoSuchFile(file) => py_warnings.append(
273 py,
274 PyBytes::new(py, &get_bytes_from_path(&file)).into_object(),
275 ),
276 }
277 }
127
278
128 Ok((lookup, modified, added, removed, deleted, unknown, clean))
279 Ok(PyTuple::new(
280 py,
281 &[
282 lookup.into_object(),
283 modified.into_object(),
284 added.into_object(),
285 removed.into_object(),
286 deleted.into_object(),
287 clean.into_object(),
288 ignored.into_object(),
289 unknown.into_object(),
290 py_warnings.into_object(),
291 bad.into_object(),
292 ][..],
293 ))
129 }
294 }
@@ -40,3 +40,5 b' impl GraphError {'
40 }
40 }
41
41
42 py_exception!(rustext, HgPathPyError, RuntimeError);
42 py_exception!(rustext, HgPathPyError, RuntimeError);
43 py_exception!(rustext, FallbackError, RuntimeError);
44 py_exception!(shared_ref, AlreadyBorrowed, RuntimeError);
General Comments 0
You need to be logged in to leave comments. Login now