##// END OF EJS Templates
rust: simplify overly complicated expression...
Raphaël Gomès -
r42761:11c025c8 default
parent child Browse files
Show More
@@ -1,335 +1,334 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
12
13 use cpython::{
13 use cpython::{
14 exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject,
14 exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject,
15 PyResult, PySequence, PyTuple, Python, PythonObject, ToPyObject,
15 PyResult, PySequence, PyTuple, Python, PythonObject, ToPyObject,
16 };
16 };
17 use hg::{
17 use hg::{
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirsIterable, DirsMultiset,
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirsIterable, DirsMultiset,
19 DirstateEntry, DirstateMapError, DirstatePackError, DirstateParents,
19 DirstateEntry, DirstateMapError, DirstatePackError, DirstateParents,
20 DirstateParseError, DirstateVec,
20 DirstateParseError, DirstateVec,
21 };
21 };
22 use std::collections::HashMap;
22 use std::collections::HashMap;
23 use std::ffi::CStr;
23 use std::ffi::CStr;
24
24
25 #[cfg(feature = "python27")]
25 #[cfg(feature = "python27")]
26 extern crate python27_sys as python_sys;
26 extern crate python27_sys as python_sys;
27 #[cfg(feature = "python3")]
27 #[cfg(feature = "python3")]
28 extern crate python3_sys as python_sys;
28 extern crate python3_sys as python_sys;
29
29
30 use self::python_sys::PyCapsule_Import;
30 use self::python_sys::PyCapsule_Import;
31 use libc::{c_char, c_int};
31 use libc::{c_char, c_int};
32 use std::cell::RefCell;
32 use std::cell::RefCell;
33 use std::mem::transmute;
33 use std::mem::transmute;
34
34
35 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
35 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
36 /// for this type, and raises a Python `Exception` if the check does not pass.
36 /// for this type, and raises a Python `Exception` if the check does not pass.
37 /// Because this type differs only in name from the regular Python tuple, it
37 /// Because this type differs only in name from the regular Python tuple, it
38 /// would be a good idea in the near future to remove it entirely to allow
38 /// would be a good idea in the near future to remove it entirely to allow
39 /// for a pure Python tuple of the same effective structure to be used,
39 /// for a pure Python tuple of the same effective structure to be used,
40 /// rendering this type and the capsule below useless.
40 /// rendering this type and the capsule below useless.
41 type MakeDirstateTupleFn = extern "C" fn(
41 type MakeDirstateTupleFn = extern "C" fn(
42 state: c_char,
42 state: c_char,
43 mode: c_int,
43 mode: c_int,
44 size: c_int,
44 size: c_int,
45 mtime: c_int,
45 mtime: c_int,
46 ) -> PyObject;
46 ) -> PyObject;
47
47
48 /// This is largely a copy/paste from cindex.rs, pending the merge of a
48 /// This is largely a copy/paste from cindex.rs, pending the merge of a
49 /// `py_capsule_fn!` macro in the rust-cpython project:
49 /// `py_capsule_fn!` macro in the rust-cpython project:
50 /// https://github.com/dgrunwald/rust-cpython/pull/169
50 /// https://github.com/dgrunwald/rust-cpython/pull/169
51 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
51 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
52 unsafe {
52 unsafe {
53 let caps_name = CStr::from_bytes_with_nul_unchecked(
53 let caps_name = CStr::from_bytes_with_nul_unchecked(
54 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
54 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
55 );
55 );
56 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
56 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
57 if from_caps.is_null() {
57 if from_caps.is_null() {
58 return Err(PyErr::fetch(py));
58 return Err(PyErr::fetch(py));
59 }
59 }
60 Ok(transmute(from_caps))
60 Ok(transmute(from_caps))
61 }
61 }
62 }
62 }
63
63
64 fn parse_dirstate_wrapper(
64 fn parse_dirstate_wrapper(
65 py: Python,
65 py: Python,
66 dmap: PyDict,
66 dmap: PyDict,
67 copymap: PyDict,
67 copymap: PyDict,
68 st: PyBytes,
68 st: PyBytes,
69 ) -> PyResult<PyTuple> {
69 ) -> PyResult<PyTuple> {
70 match parse_dirstate(st.data(py)) {
70 match parse_dirstate(st.data(py)) {
71 Ok((parents, dirstate_vec, copies)) => {
71 Ok((parents, dirstate_vec, copies)) => {
72 for (filename, entry) in dirstate_vec {
72 for (filename, entry) in dirstate_vec {
73 dmap.set_item(
73 dmap.set_item(
74 py,
74 py,
75 PyBytes::new(py, &filename[..]),
75 PyBytes::new(py, &filename[..]),
76 decapsule_make_dirstate_tuple(py)?(
76 decapsule_make_dirstate_tuple(py)?(
77 entry.state as c_char,
77 entry.state as c_char,
78 entry.mode,
78 entry.mode,
79 entry.size,
79 entry.size,
80 entry.mtime,
80 entry.mtime,
81 ),
81 ),
82 )?;
82 )?;
83 }
83 }
84 for CopyVecEntry { path, copy_path } in copies {
84 for CopyVecEntry { path, copy_path } in copies {
85 copymap.set_item(
85 copymap.set_item(
86 py,
86 py,
87 PyBytes::new(py, path),
87 PyBytes::new(py, path),
88 PyBytes::new(py, copy_path),
88 PyBytes::new(py, copy_path),
89 )?;
89 )?;
90 }
90 }
91 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
91 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
92 .to_py_object(py))
92 .to_py_object(py))
93 }
93 }
94 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
94 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
95 py,
95 py,
96 match e {
96 match e {
97 DirstateParseError::TooLittleData => {
97 DirstateParseError::TooLittleData => {
98 "too little data for parents".to_string()
98 "too little data for parents".to_string()
99 }
99 }
100 DirstateParseError::Overflow => {
100 DirstateParseError::Overflow => {
101 "overflow in dirstate".to_string()
101 "overflow in dirstate".to_string()
102 }
102 }
103 DirstateParseError::CorruptedEntry(e) => e,
103 DirstateParseError::CorruptedEntry(e) => e,
104 },
104 },
105 )),
105 )),
106 }
106 }
107 }
107 }
108
108
109 fn extract_dirstate_vec(
109 fn extract_dirstate_vec(
110 py: Python,
110 py: Python,
111 dmap: &PyDict,
111 dmap: &PyDict,
112 ) -> Result<DirstateVec, PyErr> {
112 ) -> Result<DirstateVec, PyErr> {
113 dmap.items(py)
113 dmap.items(py)
114 .iter()
114 .iter()
115 .map(|(filename, stats)| {
115 .map(|(filename, stats)| {
116 let stats = stats.extract::<PySequence>(py)?;
116 let stats = stats.extract::<PySequence>(py)?;
117 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
117 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
118 let state = state.data(py)[0] as i8;
118 let state = state.data(py)[0] as i8;
119 let mode = stats.get_item(py, 1)?.extract(py)?;
119 let mode = stats.get_item(py, 1)?.extract(py)?;
120 let size = stats.get_item(py, 2)?.extract(py)?;
120 let size = stats.get_item(py, 2)?.extract(py)?;
121 let mtime = stats.get_item(py, 3)?.extract(py)?;
121 let mtime = stats.get_item(py, 3)?.extract(py)?;
122 let filename = filename.extract::<PyBytes>(py)?;
122 let filename = filename.extract::<PyBytes>(py)?;
123 let filename = filename.data(py);
123 let filename = filename.data(py);
124 Ok((
124 Ok((
125 filename.to_owned(),
125 filename.to_owned(),
126 DirstateEntry {
126 DirstateEntry {
127 state,
127 state,
128 mode,
128 mode,
129 size,
129 size,
130 mtime,
130 mtime,
131 },
131 },
132 ))
132 ))
133 })
133 })
134 .collect()
134 .collect()
135 }
135 }
136
136
137 fn pack_dirstate_wrapper(
137 fn pack_dirstate_wrapper(
138 py: Python,
138 py: Python,
139 dmap: PyDict,
139 dmap: PyDict,
140 copymap: PyDict,
140 copymap: PyDict,
141 pl: PyTuple,
141 pl: PyTuple,
142 now: PyInt,
142 now: PyInt,
143 ) -> PyResult<PyBytes> {
143 ) -> PyResult<PyBytes> {
144 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
144 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
145 let p1: &[u8] = p1.data(py);
145 let p1: &[u8] = p1.data(py);
146 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
146 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
147 let p2: &[u8] = p2.data(py);
147 let p2: &[u8] = p2.data(py);
148
148
149 let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
149 let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
150
150
151 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
151 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
152 .items(py)
152 .items(py)
153 .iter()
153 .iter()
154 .map(|(key, value)| {
154 .map(|(key, value)| {
155 Ok((
155 Ok((
156 key.extract::<PyBytes>(py)?.data(py).to_owned(),
156 key.extract::<PyBytes>(py)?.data(py).to_owned(),
157 value.extract::<PyBytes>(py)?.data(py).to_owned(),
157 value.extract::<PyBytes>(py)?.data(py).to_owned(),
158 ))
158 ))
159 })
159 })
160 .collect();
160 .collect();
161
161
162 match pack_dirstate(
162 match pack_dirstate(
163 &dirstate_vec,
163 &dirstate_vec,
164 &copies?,
164 &copies?,
165 DirstateParents { p1, p2 },
165 DirstateParents { p1, p2 },
166 now.as_object().extract::<i32>(py)?,
166 now.as_object().extract::<i32>(py)?,
167 ) {
167 ) {
168 Ok((packed, new_dirstate_vec)) => {
168 Ok((packed, new_dirstate_vec)) => {
169 for (
169 for (
170 filename,
170 filename,
171 DirstateEntry {
171 DirstateEntry {
172 state,
172 state,
173 mode,
173 mode,
174 size,
174 size,
175 mtime,
175 mtime,
176 },
176 },
177 ) in new_dirstate_vec
177 ) in new_dirstate_vec
178 {
178 {
179 dmap.set_item(
179 dmap.set_item(
180 py,
180 py,
181 PyBytes::new(py, &filename[..]),
181 PyBytes::new(py, &filename[..]),
182 decapsule_make_dirstate_tuple(py)?(
182 decapsule_make_dirstate_tuple(py)?(
183 state as c_char,
183 state as c_char,
184 mode,
184 mode,
185 size,
185 size,
186 mtime,
186 mtime,
187 ),
187 ),
188 )?;
188 )?;
189 }
189 }
190 Ok(PyBytes::new(py, &packed))
190 Ok(PyBytes::new(py, &packed))
191 }
191 }
192 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
192 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
193 py,
193 py,
194 match error {
194 match error {
195 DirstatePackError::CorruptedParent => {
195 DirstatePackError::CorruptedParent => {
196 "expected a 20-byte hash".to_string()
196 "expected a 20-byte hash".to_string()
197 }
197 }
198 DirstatePackError::CorruptedEntry(e) => e,
198 DirstatePackError::CorruptedEntry(e) => e,
199 DirstatePackError::BadSize(expected, actual) => {
199 DirstatePackError::BadSize(expected, actual) => {
200 format!("bad dirstate size: {} != {}", actual, expected)
200 format!("bad dirstate size: {} != {}", actual, expected)
201 }
201 }
202 },
202 },
203 )),
203 )),
204 }
204 }
205 }
205 }
206
206
207 py_class!(pub class Dirs |py| {
207 py_class!(pub class Dirs |py| {
208 data dirs_map: RefCell<DirsMultiset>;
208 data dirs_map: RefCell<DirsMultiset>;
209
209
210 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
210 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
211 // a `list`)
211 // a `list`)
212 def __new__(
212 def __new__(
213 _cls,
213 _cls,
214 map: PyObject,
214 map: PyObject,
215 skip: Option<PyObject> = None
215 skip: Option<PyObject> = None
216 ) -> PyResult<Self> {
216 ) -> PyResult<Self> {
217 let mut skip_state: Option<i8> = None;
217 let mut skip_state: Option<i8> = None;
218 if let Some(skip) = skip {
218 if let Some(skip) = skip {
219 skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8);
219 skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8);
220 }
220 }
221 let dirs_map;
221 let dirs_map;
222
222
223 if let Ok(map) = map.cast_as::<PyDict>(py) {
223 if let Ok(map) = map.cast_as::<PyDict>(py) {
224 let dirstate_vec = extract_dirstate_vec(py, &map)?;
224 let dirstate_vec = extract_dirstate_vec(py, &map)?;
225 dirs_map = DirsMultiset::new(
225 dirs_map = DirsMultiset::new(
226 DirsIterable::Dirstate(dirstate_vec),
226 DirsIterable::Dirstate(dirstate_vec),
227 skip_state,
227 skip_state,
228 )
228 )
229 } else {
229 } else {
230 let map: Result<Vec<Vec<u8>>, PyErr> = map
230 let map: Result<Vec<Vec<u8>>, PyErr> = map
231 .iter(py)?
231 .iter(py)?
232 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
232 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
233 .collect();
233 .collect();
234 dirs_map = DirsMultiset::new(
234 dirs_map = DirsMultiset::new(
235 DirsIterable::Manifest(map?),
235 DirsIterable::Manifest(map?),
236 skip_state,
236 skip_state,
237 )
237 )
238 }
238 }
239
239
240 Self::create_instance(py, RefCell::new(dirs_map))
240 Self::create_instance(py, RefCell::new(dirs_map))
241 }
241 }
242
242
243 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
243 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
244 self.dirs_map(py).borrow_mut().add_path(
244 self.dirs_map(py).borrow_mut().add_path(
245 path.extract::<PyBytes>(py)?.data(py),
245 path.extract::<PyBytes>(py)?.data(py),
246 );
246 );
247 Ok(py.None())
247 Ok(py.None())
248 }
248 }
249
249
250 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
250 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
251 self.dirs_map(py).borrow_mut().delete_path(
251 self.dirs_map(py).borrow_mut().delete_path(
252 path.extract::<PyBytes>(py)?.data(py),
252 path.extract::<PyBytes>(py)?.data(py),
253 )
253 )
254 .and(Ok(py.None()))
254 .and(Ok(py.None()))
255 .or_else(|e| {
255 .or_else(|e| {
256 match e {
256 match e {
257 DirstateMapError::PathNotFound(_p) => {
257 DirstateMapError::PathNotFound(_p) => {
258 Err(PyErr::new::<exc::ValueError, _>(
258 Err(PyErr::new::<exc::ValueError, _>(
259 py,
259 py,
260 "expected a value, found none".to_string(),
260 "expected a value, found none".to_string(),
261 ))
261 ))
262 }
262 }
263 DirstateMapError::EmptyPath => {
263 DirstateMapError::EmptyPath => {
264 Ok(py.None())
264 Ok(py.None())
265 }
265 }
266 }
266 }
267 })
267 })
268 }
268 }
269
269
270 // This is really inefficient on top of being ugly, but it's an easy way
270 // This is really inefficient on top of being ugly, but it's an easy way
271 // of having it work to continue working on the rest of the module
271 // of having it work to continue working on the rest of the module
272 // hopefully bypassing Python entirely pretty soon.
272 // hopefully bypassing Python entirely pretty soon.
273 def __iter__(&self) -> PyResult<PyObject> {
273 def __iter__(&self) -> PyResult<PyObject> {
274 let dict = PyDict::new(py);
274 let dict = PyDict::new(py);
275
275
276 for (key, value) in self.dirs_map(py).borrow().iter() {
276 for (key, value) in self.dirs_map(py).borrow().iter() {
277 dict.set_item(
277 dict.set_item(
278 py,
278 py,
279 PyBytes::new(py, &key[..]),
279 PyBytes::new(py, &key[..]),
280 value.to_py_object(py),
280 value.to_py_object(py),
281 )?;
281 )?;
282 }
282 }
283
283
284 let locals = PyDict::new(py);
284 let locals = PyDict::new(py);
285 locals.set_item(py, "obj", dict)?;
285 locals.set_item(py, "obj", dict)?;
286
286
287 py.eval("iter(obj)", None, Some(&locals))
287 py.eval("iter(obj)", None, Some(&locals))
288 }
288 }
289
289
290 def __contains__(&self, item: PyObject) -> PyResult<bool> {
290 def __contains__(&self, item: PyObject) -> PyResult<bool> {
291 Ok(self
291 Ok(self
292 .dirs_map(py)
292 .dirs_map(py)
293 .borrow()
293 .borrow()
294 .get(&item.extract::<PyBytes>(py)?.data(py).to_owned())
294 .contains_key(item.extract::<PyBytes>(py)?.data(py).as_ref()))
295 .is_some())
296 }
295 }
297 });
296 });
298
297
299 /// Create the module, with `__package__` given from parent
298 /// Create the module, with `__package__` given from parent
300 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
299 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
301 let dotted_name = &format!("{}.dirstate", package);
300 let dotted_name = &format!("{}.dirstate", package);
302 let m = PyModule::new(py, dotted_name)?;
301 let m = PyModule::new(py, dotted_name)?;
303
302
304 m.add(py, "__package__", package)?;
303 m.add(py, "__package__", package)?;
305 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
304 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
306 m.add(
305 m.add(
307 py,
306 py,
308 "parse_dirstate",
307 "parse_dirstate",
309 py_fn!(
308 py_fn!(
310 py,
309 py,
311 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
310 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
312 ),
311 ),
313 )?;
312 )?;
314 m.add(
313 m.add(
315 py,
314 py,
316 "pack_dirstate",
315 "pack_dirstate",
317 py_fn!(
316 py_fn!(
318 py,
317 py,
319 pack_dirstate_wrapper(
318 pack_dirstate_wrapper(
320 dmap: PyDict,
319 dmap: PyDict,
321 copymap: PyDict,
320 copymap: PyDict,
322 pl: PyTuple,
321 pl: PyTuple,
323 now: PyInt
322 now: PyInt
324 )
323 )
325 ),
324 ),
326 )?;
325 )?;
327
326
328 m.add_class::<Dirs>(py)?;
327 m.add_class::<Dirs>(py)?;
329
328
330 let sys = PyModule::import(py, "sys")?;
329 let sys = PyModule::import(py, "sys")?;
331 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
330 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
332 sys_modules.set_item(py, dotted_name, &m)?;
331 sys_modules.set_item(py, dotted_name, &m)?;
333
332
334 Ok(m)
333 Ok(m)
335 }
334 }
General Comments 0
You need to be logged in to leave comments. Login now