##// END OF EJS Templates
rust-hg-cpython: update status bridge with the new `traversedir` support...
Raphaël Gomès -
r45354:01afda7e default
parent child Browse files
Show More
@@ -1,146 +1,147 b''
1 // dirstate.rs
1 // dirstate.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate` module provided by the
8 //! Bindings for the `hg::dirstate` module provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12 mod copymap;
12 mod copymap;
13 mod dirs_multiset;
13 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::{
17 use crate::{
18 dirstate::{
18 dirstate::{
19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
20 },
20 },
21 exceptions,
21 exceptions,
22 };
22 };
23 use cpython::{
23 use cpython::{
24 exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
24 exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
25 PySequence, Python,
25 PySequence, Python,
26 };
26 };
27 use hg::{
27 use hg::{
28 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParseError, EntryState,
28 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParseError, EntryState,
29 StateMap,
29 StateMap,
30 };
30 };
31 use libc::{c_char, c_int};
31 use libc::{c_char, c_int};
32 use std::convert::TryFrom;
32 use std::convert::TryFrom;
33
33
34 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
34 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
35 // for this type, and raises a Python `Exception` if the check does not pass.
35 // for this type, and raises a Python `Exception` if the check does not pass.
36 // Because this type differs only in name from the regular Python tuple, it
36 // Because this type differs only in name from the regular Python tuple, it
37 // would be a good idea in the near future to remove it entirely to allow
37 // would be a good idea in the near future to remove it entirely to allow
38 // for a pure Python tuple of the same effective structure to be used,
38 // for a pure Python tuple of the same effective structure to be used,
39 // rendering this type and the capsule below useless.
39 // rendering this type and the capsule below useless.
40 py_capsule_fn!(
40 py_capsule_fn!(
41 from mercurial.cext.parsers import make_dirstate_tuple_CAPI
41 from mercurial.cext.parsers import make_dirstate_tuple_CAPI
42 as make_dirstate_tuple_capi
42 as make_dirstate_tuple_capi
43 signature (
43 signature (
44 state: c_char,
44 state: c_char,
45 mode: c_int,
45 mode: c_int,
46 size: c_int,
46 size: c_int,
47 mtime: c_int,
47 mtime: c_int,
48 ) -> *mut RawPyObject
48 ) -> *mut RawPyObject
49 );
49 );
50
50
51 pub fn make_dirstate_tuple(
51 pub fn make_dirstate_tuple(
52 py: Python,
52 py: Python,
53 entry: &DirstateEntry,
53 entry: &DirstateEntry,
54 ) -> PyResult<PyObject> {
54 ) -> PyResult<PyObject> {
55 // might be silly to retrieve capsule function in hot loop
55 // might be silly to retrieve capsule function in hot loop
56 let make = make_dirstate_tuple_capi::retrieve(py)?;
56 let make = make_dirstate_tuple_capi::retrieve(py)?;
57
57
58 let &DirstateEntry {
58 let &DirstateEntry {
59 state,
59 state,
60 mode,
60 mode,
61 size,
61 size,
62 mtime,
62 mtime,
63 } = entry;
63 } = entry;
64 // Explicitly go through u8 first, then cast to platform-specific `c_char`
64 // Explicitly go through u8 first, then cast to platform-specific `c_char`
65 // because Into<u8> has a specific implementation while `as c_char` would
65 // because Into<u8> has a specific implementation while `as c_char` would
66 // just do a naive enum cast.
66 // just do a naive enum cast.
67 let state_code: u8 = state.into();
67 let state_code: u8 = state.into();
68
68
69 let maybe_obj = unsafe {
69 let maybe_obj = unsafe {
70 let ptr = make(state_code as c_char, mode, size, mtime);
70 let ptr = make(state_code as c_char, mode, size, mtime);
71 PyObject::from_owned_ptr_opt(py, ptr)
71 PyObject::from_owned_ptr_opt(py, ptr)
72 };
72 };
73 maybe_obj.ok_or_else(|| PyErr::fetch(py))
73 maybe_obj.ok_or_else(|| PyErr::fetch(py))
74 }
74 }
75
75
76 pub fn extract_dirstate(py: Python, dmap: &PyDict) -> Result<StateMap, PyErr> {
76 pub fn extract_dirstate(py: Python, dmap: &PyDict) -> Result<StateMap, PyErr> {
77 dmap.items(py)
77 dmap.items(py)
78 .iter()
78 .iter()
79 .map(|(filename, stats)| {
79 .map(|(filename, stats)| {
80 let stats = stats.extract::<PySequence>(py)?;
80 let stats = stats.extract::<PySequence>(py)?;
81 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
81 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
82 let state = EntryState::try_from(state.data(py)[0]).map_err(
82 let state = EntryState::try_from(state.data(py)[0]).map_err(
83 |e: DirstateParseError| {
83 |e: DirstateParseError| {
84 PyErr::new::<exc::ValueError, _>(py, e.to_string())
84 PyErr::new::<exc::ValueError, _>(py, e.to_string())
85 },
85 },
86 )?;
86 )?;
87 let mode = stats.get_item(py, 1)?.extract(py)?;
87 let mode = stats.get_item(py, 1)?.extract(py)?;
88 let size = stats.get_item(py, 2)?.extract(py)?;
88 let size = stats.get_item(py, 2)?.extract(py)?;
89 let mtime = stats.get_item(py, 3)?.extract(py)?;
89 let mtime = stats.get_item(py, 3)?.extract(py)?;
90 let filename = filename.extract::<PyBytes>(py)?;
90 let filename = filename.extract::<PyBytes>(py)?;
91 let filename = filename.data(py);
91 let filename = filename.data(py);
92 Ok((
92 Ok((
93 HgPathBuf::from(filename.to_owned()),
93 HgPathBuf::from(filename.to_owned()),
94 DirstateEntry {
94 DirstateEntry {
95 state,
95 state,
96 mode,
96 mode,
97 size,
97 size,
98 mtime,
98 mtime,
99 },
99 },
100 ))
100 ))
101 })
101 })
102 .collect()
102 .collect()
103 }
103 }
104
104
105 /// Create the module, with `__package__` given from parent
105 /// Create the module, with `__package__` given from parent
106 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
106 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
107 let dotted_name = &format!("{}.dirstate", package);
107 let dotted_name = &format!("{}.dirstate", package);
108 let m = PyModule::new(py, dotted_name)?;
108 let m = PyModule::new(py, dotted_name)?;
109
109
110 simple_logger::init_by_env();
110 simple_logger::init_by_env();
111
111
112 m.add(py, "__package__", package)?;
112 m.add(py, "__package__", package)?;
113 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
113 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
114
114
115 m.add(
115 m.add(
116 py,
116 py,
117 "FallbackError",
117 "FallbackError",
118 py.get_type::<exceptions::FallbackError>(),
118 py.get_type::<exceptions::FallbackError>(),
119 )?;
119 )?;
120 m.add_class::<Dirs>(py)?;
120 m.add_class::<Dirs>(py)?;
121 m.add_class::<DirstateMap>(py)?;
121 m.add_class::<DirstateMap>(py)?;
122 m.add(
122 m.add(
123 py,
123 py,
124 "status",
124 "status",
125 py_fn!(
125 py_fn!(
126 py,
126 py,
127 status_wrapper(
127 status_wrapper(
128 dmap: DirstateMap,
128 dmap: DirstateMap,
129 root_dir: PyObject,
129 root_dir: PyObject,
130 matcher: PyObject,
130 matcher: PyObject,
131 ignorefiles: PyList,
131 ignorefiles: PyList,
132 check_exec: bool,
132 check_exec: bool,
133 last_normal_time: i64,
133 last_normal_time: i64,
134 list_clean: bool,
134 list_clean: bool,
135 list_ignored: bool,
135 list_ignored: bool,
136 list_unknown: bool
136 list_unknown: bool,
137 collect_traversed_dirs: bool
137 )
138 )
138 ),
139 ),
139 )?;
140 )?;
140
141
141 let sys = PyModule::import(py, "sys")?;
142 let sys = PyModule::import(py, "sys")?;
142 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
143 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
143 sys_modules.set_item(py, dotted_name, &m)?;
144 sys_modules.set_item(py, dotted_name, &m)?;
144
145
145 Ok(m)
146 Ok(m)
146 }
147 }
@@ -1,297 +1,303 b''
1 // status.rs
1 // status.rs
2 //
2 //
3 // Copyright 2019, Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019, Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::status` module provided by the
8 //! Bindings for the `hg::status` module provided by the
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, exceptions::FallbackError};
12 use crate::{dirstate::DirstateMap, exceptions::FallbackError};
13 use cpython::{
13 use cpython::{
14 exc::ValueError, ObjectProtocol, PyBytes, PyErr, PyList, PyObject,
14 exc::ValueError, ObjectProtocol, PyBytes, PyErr, PyList, PyObject,
15 PyResult, PyTuple, Python, PythonObject, ToPyObject,
15 PyResult, PyTuple, Python, PythonObject, ToPyObject,
16 };
16 };
17 use hg::{
17 use hg::{
18 matchers::{AlwaysMatcher, FileMatcher, IncludeMatcher},
18 matchers::{AlwaysMatcher, FileMatcher, IncludeMatcher},
19 parse_pattern_syntax, status,
19 parse_pattern_syntax, status,
20 utils::{
20 utils::{
21 files::{get_bytes_from_path, get_path_from_bytes},
21 files::{get_bytes_from_path, get_path_from_bytes},
22 hg_path::{HgPath, HgPathBuf},
22 hg_path::{HgPath, HgPathBuf},
23 },
23 },
24 BadMatch, DirstateStatus, IgnorePattern, PatternFileWarning, StatusError,
24 BadMatch, DirstateStatus, IgnorePattern, PatternFileWarning, StatusError,
25 StatusOptions,
25 StatusOptions,
26 };
26 };
27 use std::borrow::{Borrow, Cow};
27 use std::borrow::{Borrow, Cow};
28
28
29 /// 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`
30 /// upstream.
30 /// upstream.
31 fn collect_pybytes_list(
31 fn collect_pybytes_list(
32 py: Python,
32 py: Python,
33 collection: &[impl AsRef<HgPath>],
33 collection: &[impl AsRef<HgPath>],
34 ) -> PyList {
34 ) -> PyList {
35 let list = PyList::new(py, &[]);
35 let list = PyList::new(py, &[]);
36
36
37 for path in collection.iter() {
37 for path in collection.iter() {
38 list.append(
38 list.append(
39 py,
39 py,
40 PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
40 PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
41 )
41 )
42 }
42 }
43
43
44 list
44 list
45 }
45 }
46
46
47 fn collect_bad_matches(
47 fn collect_bad_matches(
48 py: Python,
48 py: Python,
49 collection: &[(impl AsRef<HgPath>, BadMatch)],
49 collection: &[(impl AsRef<HgPath>, BadMatch)],
50 ) -> PyResult<PyList> {
50 ) -> PyResult<PyList> {
51 let list = PyList::new(py, &[]);
51 let list = PyList::new(py, &[]);
52
52
53 let os = py.import("os")?;
53 let os = py.import("os")?;
54 let get_error_message = |code: i32| -> PyResult<_> {
54 let get_error_message = |code: i32| -> PyResult<_> {
55 os.call(
55 os.call(
56 py,
56 py,
57 "strerror",
57 "strerror",
58 PyTuple::new(py, &[code.to_py_object(py).into_object()]),
58 PyTuple::new(py, &[code.to_py_object(py).into_object()]),
59 None,
59 None,
60 )
60 )
61 };
61 };
62
62
63 for (path, bad_match) in collection.iter() {
63 for (path, bad_match) in collection.iter() {
64 let message = match bad_match {
64 let message = match bad_match {
65 BadMatch::OsError(code) => get_error_message(*code)?,
65 BadMatch::OsError(code) => get_error_message(*code)?,
66 BadMatch::BadType(bad_type) => format!(
66 BadMatch::BadType(bad_type) => format!(
67 "unsupported file type (type is {})",
67 "unsupported file type (type is {})",
68 bad_type.to_string()
68 bad_type.to_string()
69 )
69 )
70 .to_py_object(py)
70 .to_py_object(py)
71 .into_object(),
71 .into_object(),
72 };
72 };
73 list.append(
73 list.append(
74 py,
74 py,
75 (PyBytes::new(py, path.as_ref().as_bytes()), message)
75 (PyBytes::new(py, path.as_ref().as_bytes()), message)
76 .to_py_object(py)
76 .to_py_object(py)
77 .into_object(),
77 .into_object(),
78 )
78 )
79 }
79 }
80
80
81 Ok(list)
81 Ok(list)
82 }
82 }
83
83
84 fn handle_fallback(py: Python, err: StatusError) -> PyErr {
84 fn handle_fallback(py: Python, err: StatusError) -> PyErr {
85 match err {
85 match err {
86 StatusError::Pattern(e) => {
86 StatusError::Pattern(e) => {
87 let as_string = e.to_string();
87 let as_string = e.to_string();
88 log::trace!("Rust status fallback: `{}`", &as_string);
88 log::trace!("Rust status fallback: `{}`", &as_string);
89
89
90 PyErr::new::<FallbackError, _>(py, &as_string)
90 PyErr::new::<FallbackError, _>(py, &as_string)
91 }
91 }
92 e => PyErr::new::<ValueError, _>(py, e.to_string()),
92 e => PyErr::new::<ValueError, _>(py, e.to_string()),
93 }
93 }
94 }
94 }
95
95
96 pub fn status_wrapper(
96 pub fn status_wrapper(
97 py: Python,
97 py: Python,
98 dmap: DirstateMap,
98 dmap: DirstateMap,
99 matcher: PyObject,
99 matcher: PyObject,
100 root_dir: PyObject,
100 root_dir: PyObject,
101 ignore_files: PyList,
101 ignore_files: PyList,
102 check_exec: bool,
102 check_exec: bool,
103 last_normal_time: i64,
103 last_normal_time: i64,
104 list_clean: bool,
104 list_clean: bool,
105 list_ignored: bool,
105 list_ignored: bool,
106 list_unknown: bool,
106 list_unknown: bool,
107 collect_traversed_dirs: bool,
107 ) -> PyResult<PyTuple> {
108 ) -> PyResult<PyTuple> {
108 let bytes = root_dir.extract::<PyBytes>(py)?;
109 let bytes = root_dir.extract::<PyBytes>(py)?;
109 let root_dir = get_path_from_bytes(bytes.data(py));
110 let root_dir = get_path_from_bytes(bytes.data(py));
110
111
111 let dmap: DirstateMap = dmap.to_py_object(py);
112 let dmap: DirstateMap = dmap.to_py_object(py);
112 let dmap = dmap.get_inner(py);
113 let dmap = dmap.get_inner(py);
113
114
114 let ignore_files: PyResult<Vec<_>> = ignore_files
115 let ignore_files: PyResult<Vec<_>> = ignore_files
115 .iter(py)
116 .iter(py)
116 .map(|b| {
117 .map(|b| {
117 let file = b.extract::<PyBytes>(py)?;
118 let file = b.extract::<PyBytes>(py)?;
118 Ok(get_path_from_bytes(file.data(py)).to_owned())
119 Ok(get_path_from_bytes(file.data(py)).to_owned())
119 })
120 })
120 .collect();
121 .collect();
121 let ignore_files = ignore_files?;
122 let ignore_files = ignore_files?;
122
123
123 match matcher.get_type(py).name(py).borrow() {
124 match matcher.get_type(py).name(py).borrow() {
124 "alwaysmatcher" => {
125 "alwaysmatcher" => {
125 let matcher = AlwaysMatcher;
126 let matcher = AlwaysMatcher;
126 let ((lookup, status_res), warnings) = status(
127 let ((lookup, status_res), warnings) = status(
127 &dmap,
128 &dmap,
128 &matcher,
129 &matcher,
129 &root_dir,
130 &root_dir,
130 ignore_files,
131 ignore_files,
131 StatusOptions {
132 StatusOptions {
132 check_exec,
133 check_exec,
133 last_normal_time,
134 last_normal_time,
134 list_clean,
135 list_clean,
135 list_ignored,
136 list_ignored,
136 list_unknown,
137 list_unknown,
138 collect_traversed_dirs,
137 },
139 },
138 )
140 )
139 .map_err(|e| handle_fallback(py, e))?;
141 .map_err(|e| handle_fallback(py, e))?;
140 build_response(py, lookup, status_res, warnings)
142 build_response(py, lookup, status_res, warnings)
141 }
143 }
142 "exactmatcher" => {
144 "exactmatcher" => {
143 let files = matcher.call_method(
145 let files = matcher.call_method(
144 py,
146 py,
145 "files",
147 "files",
146 PyTuple::new(py, &[]),
148 PyTuple::new(py, &[]),
147 None,
149 None,
148 )?;
150 )?;
149 let files: PyList = files.cast_into(py)?;
151 let files: PyList = files.cast_into(py)?;
150 let files: PyResult<Vec<HgPathBuf>> = files
152 let files: PyResult<Vec<HgPathBuf>> = files
151 .iter(py)
153 .iter(py)
152 .map(|f| {
154 .map(|f| {
153 Ok(HgPathBuf::from_bytes(
155 Ok(HgPathBuf::from_bytes(
154 f.extract::<PyBytes>(py)?.data(py),
156 f.extract::<PyBytes>(py)?.data(py),
155 ))
157 ))
156 })
158 })
157 .collect();
159 .collect();
158
160
159 let files = files?;
161 let files = files?;
160 let matcher = FileMatcher::new(&files)
162 let matcher = FileMatcher::new(&files)
161 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
163 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
162 let ((lookup, status_res), warnings) = status(
164 let ((lookup, status_res), warnings) = status(
163 &dmap,
165 &dmap,
164 &matcher,
166 &matcher,
165 &root_dir,
167 &root_dir,
166 ignore_files,
168 ignore_files,
167 StatusOptions {
169 StatusOptions {
168 check_exec,
170 check_exec,
169 last_normal_time,
171 last_normal_time,
170 list_clean,
172 list_clean,
171 list_ignored,
173 list_ignored,
172 list_unknown,
174 list_unknown,
175 collect_traversed_dirs,
173 },
176 },
174 )
177 )
175 .map_err(|e| handle_fallback(py, e))?;
178 .map_err(|e| handle_fallback(py, e))?;
176 build_response(py, lookup, status_res, warnings)
179 build_response(py, lookup, status_res, warnings)
177 }
180 }
178 "includematcher" => {
181 "includematcher" => {
179 // Get the patterns from Python even though most of them are
182 // Get the patterns from Python even though most of them are
180 // redundant with those we will parse later on, as they include
183 // redundant with those we will parse later on, as they include
181 // those passed from the command line.
184 // those passed from the command line.
182 let ignore_patterns: PyResult<Vec<_>> = matcher
185 let ignore_patterns: PyResult<Vec<_>> = matcher
183 .getattr(py, "_kindpats")?
186 .getattr(py, "_kindpats")?
184 .iter(py)?
187 .iter(py)?
185 .map(|k| {
188 .map(|k| {
186 let k = k?;
189 let k = k?;
187 let syntax = parse_pattern_syntax(
190 let syntax = parse_pattern_syntax(
188 &[
191 &[
189 k.get_item(py, 0)?
192 k.get_item(py, 0)?
190 .extract::<PyBytes>(py)?
193 .extract::<PyBytes>(py)?
191 .data(py),
194 .data(py),
192 &b":"[..],
195 &b":"[..],
193 ]
196 ]
194 .concat(),
197 .concat(),
195 )
198 )
196 .map_err(|e| {
199 .map_err(|e| {
197 handle_fallback(py, StatusError::Pattern(e))
200 handle_fallback(py, StatusError::Pattern(e))
198 })?;
201 })?;
199 let pattern = k.get_item(py, 1)?.extract::<PyBytes>(py)?;
202 let pattern = k.get_item(py, 1)?.extract::<PyBytes>(py)?;
200 let pattern = pattern.data(py);
203 let pattern = pattern.data(py);
201 let source = k.get_item(py, 2)?.extract::<PyBytes>(py)?;
204 let source = k.get_item(py, 2)?.extract::<PyBytes>(py)?;
202 let source = get_path_from_bytes(source.data(py));
205 let source = get_path_from_bytes(source.data(py));
203 let new = IgnorePattern::new(syntax, pattern, source);
206 let new = IgnorePattern::new(syntax, pattern, source);
204 Ok(new)
207 Ok(new)
205 })
208 })
206 .collect();
209 .collect();
207
210
208 let ignore_patterns = ignore_patterns?;
211 let ignore_patterns = ignore_patterns?;
209 let mut all_warnings = vec![];
212 let mut all_warnings = vec![];
210
213
211 let (matcher, warnings) =
214 let (matcher, warnings) =
212 IncludeMatcher::new(ignore_patterns, &root_dir)
215 IncludeMatcher::new(ignore_patterns, &root_dir)
213 .map_err(|e| handle_fallback(py, e.into()))?;
216 .map_err(|e| handle_fallback(py, e.into()))?;
214 all_warnings.extend(warnings);
217 all_warnings.extend(warnings);
215
218
216 let ((lookup, status_res), warnings) = status(
219 let ((lookup, status_res), warnings) = status(
217 &dmap,
220 &dmap,
218 &matcher,
221 &matcher,
219 &root_dir,
222 &root_dir,
220 ignore_files,
223 ignore_files,
221 StatusOptions {
224 StatusOptions {
222 check_exec,
225 check_exec,
223 last_normal_time,
226 last_normal_time,
224 list_clean,
227 list_clean,
225 list_ignored,
228 list_ignored,
226 list_unknown,
229 list_unknown,
230 collect_traversed_dirs,
227 },
231 },
228 )
232 )
229 .map_err(|e| handle_fallback(py, e))?;
233 .map_err(|e| handle_fallback(py, e))?;
230
234
231 all_warnings.extend(warnings);
235 all_warnings.extend(warnings);
232
236
233 build_response(py, lookup, status_res, all_warnings)
237 build_response(py, lookup, status_res, all_warnings)
234 }
238 }
235 e => {
239 e => {
236 return Err(PyErr::new::<ValueError, _>(
240 return Err(PyErr::new::<ValueError, _>(
237 py,
241 py,
238 format!("Unsupported matcher {}", e),
242 format!("Unsupported matcher {}", e),
239 ));
243 ));
240 }
244 }
241 }
245 }
242 }
246 }
243
247
244 fn build_response(
248 fn build_response(
245 py: Python,
249 py: Python,
246 lookup: Vec<Cow<HgPath>>,
250 lookup: Vec<Cow<HgPath>>,
247 status_res: DirstateStatus,
251 status_res: DirstateStatus,
248 warnings: Vec<PatternFileWarning>,
252 warnings: Vec<PatternFileWarning>,
249 ) -> PyResult<PyTuple> {
253 ) -> PyResult<PyTuple> {
250 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
254 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
251 let added = collect_pybytes_list(py, status_res.added.as_ref());
255 let added = collect_pybytes_list(py, status_res.added.as_ref());
252 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
256 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
253 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
257 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
254 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
258 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
255 let ignored = collect_pybytes_list(py, status_res.ignored.as_ref());
259 let ignored = collect_pybytes_list(py, status_res.ignored.as_ref());
256 let unknown = collect_pybytes_list(py, status_res.unknown.as_ref());
260 let unknown = collect_pybytes_list(py, status_res.unknown.as_ref());
257 let lookup = collect_pybytes_list(py, lookup.as_ref());
261 let lookup = collect_pybytes_list(py, lookup.as_ref());
258 let bad = collect_bad_matches(py, status_res.bad.as_ref())?;
262 let bad = collect_bad_matches(py, status_res.bad.as_ref())?;
263 let traversed = collect_pybytes_list(py, status_res.traversed.as_ref());
259 let py_warnings = PyList::new(py, &[]);
264 let py_warnings = PyList::new(py, &[]);
260 for warning in warnings.iter() {
265 for warning in warnings.iter() {
261 // We use duck-typing on the Python side for dispatch, good enough for
266 // We use duck-typing on the Python side for dispatch, good enough for
262 // now.
267 // now.
263 match warning {
268 match warning {
264 PatternFileWarning::InvalidSyntax(file, syn) => {
269 PatternFileWarning::InvalidSyntax(file, syn) => {
265 py_warnings.append(
270 py_warnings.append(
266 py,
271 py,
267 (
272 (
268 PyBytes::new(py, &get_bytes_from_path(&file)),
273 PyBytes::new(py, &get_bytes_from_path(&file)),
269 PyBytes::new(py, syn),
274 PyBytes::new(py, syn),
270 )
275 )
271 .to_py_object(py)
276 .to_py_object(py)
272 .into_object(),
277 .into_object(),
273 );
278 );
274 }
279 }
275 PatternFileWarning::NoSuchFile(file) => py_warnings.append(
280 PatternFileWarning::NoSuchFile(file) => py_warnings.append(
276 py,
281 py,
277 PyBytes::new(py, &get_bytes_from_path(&file)).into_object(),
282 PyBytes::new(py, &get_bytes_from_path(&file)).into_object(),
278 ),
283 ),
279 }
284 }
280 }
285 }
281
286
282 Ok(PyTuple::new(
287 Ok(PyTuple::new(
283 py,
288 py,
284 &[
289 &[
285 lookup.into_object(),
290 lookup.into_object(),
286 modified.into_object(),
291 modified.into_object(),
287 added.into_object(),
292 added.into_object(),
288 removed.into_object(),
293 removed.into_object(),
289 deleted.into_object(),
294 deleted.into_object(),
290 clean.into_object(),
295 clean.into_object(),
291 ignored.into_object(),
296 ignored.into_object(),
292 unknown.into_object(),
297 unknown.into_object(),
293 py_warnings.into_object(),
298 py_warnings.into_object(),
294 bad.into_object(),
299 bad.into_object(),
300 traversed.into_object(),
295 ][..],
301 ][..],
296 ))
302 ))
297 }
303 }
General Comments 0
You need to be logged in to leave comments. Login now