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 mod dirs_multiset; |
|
13 use crate::dirstate::dirs_multiset::Dirs; |
13 use cpython::{ |
14 use cpython::{ |
14 exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, |
15 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult, |
15 PyResult, PySequence, PyTuple, Python, PythonObject, ToPyObject, |
16 PySequence, PyTuple, Python, PythonObject, ToPyObject, |
16 }; |
17 }; |
17 use hg::{ |
18 use hg::{ |
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirsIterable, DirsMultiset, |
19 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry, |
19 DirstateEntry, DirstateMapError, DirstatePackError, DirstateParents, |
20 DirstatePackError, DirstateParents, DirstateParseError, DirstateVec, |
20 DirstateParseError, DirstateVec, |
|
21 }; |
21 }; |
22 use libc::{c_char, c_int}; |
22 use libc::{c_char, c_int}; |
23 #[cfg(feature = "python27")] |
23 #[cfg(feature = "python27")] |
24 use python27_sys::PyCapsule_Import; |
24 use python27_sys::PyCapsule_Import; |
25 #[cfg(feature = "python3")] |
25 #[cfg(feature = "python3")] |
26 use python3_sys::PyCapsule_Import; |
26 use python3_sys::PyCapsule_Import; |
27 use std::cell::RefCell; |
|
28 use std::collections::HashMap; |
27 use std::collections::HashMap; |
29 use std::ffi::CStr; |
28 use std::ffi::CStr; |
30 use std::mem::transmute; |
29 use std::mem::transmute; |
31 |
30 |
32 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances |
31 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances |
199 }, |
198 }, |
200 )), |
199 )), |
201 } |
200 } |
202 } |
201 } |
203 |
202 |
204 py_class!(pub class Dirs |py| { |
|
205 data dirs_map: RefCell<DirsMultiset>; |
|
206 |
|
207 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes |
|
208 // a `list`) |
|
209 def __new__( |
|
210 _cls, |
|
211 map: PyObject, |
|
212 skip: Option<PyObject> = None |
|
213 ) -> PyResult<Self> { |
|
214 let mut skip_state: Option<i8> = None; |
|
215 if let Some(skip) = skip { |
|
216 skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8); |
|
217 } |
|
218 let dirs_map; |
|
219 |
|
220 if let Ok(map) = map.cast_as::<PyDict>(py) { |
|
221 let dirstate_vec = extract_dirstate_vec(py, &map)?; |
|
222 dirs_map = DirsMultiset::new( |
|
223 DirsIterable::Dirstate(dirstate_vec), |
|
224 skip_state, |
|
225 ) |
|
226 } else { |
|
227 let map: Result<Vec<Vec<u8>>, PyErr> = map |
|
228 .iter(py)? |
|
229 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned())) |
|
230 .collect(); |
|
231 dirs_map = DirsMultiset::new( |
|
232 DirsIterable::Manifest(map?), |
|
233 skip_state, |
|
234 ) |
|
235 } |
|
236 |
|
237 Self::create_instance(py, RefCell::new(dirs_map)) |
|
238 } |
|
239 |
|
240 def addpath(&self, path: PyObject) -> PyResult<PyObject> { |
|
241 self.dirs_map(py).borrow_mut().add_path( |
|
242 path.extract::<PyBytes>(py)?.data(py), |
|
243 ); |
|
244 Ok(py.None()) |
|
245 } |
|
246 |
|
247 def delpath(&self, path: PyObject) -> PyResult<PyObject> { |
|
248 self.dirs_map(py).borrow_mut().delete_path( |
|
249 path.extract::<PyBytes>(py)?.data(py), |
|
250 ) |
|
251 .and(Ok(py.None())) |
|
252 .or_else(|e| { |
|
253 match e { |
|
254 DirstateMapError::PathNotFound(_p) => { |
|
255 Err(PyErr::new::<exc::ValueError, _>( |
|
256 py, |
|
257 "expected a value, found none".to_string(), |
|
258 )) |
|
259 } |
|
260 DirstateMapError::EmptyPath => { |
|
261 Ok(py.None()) |
|
262 } |
|
263 } |
|
264 }) |
|
265 } |
|
266 |
|
267 // This is really inefficient on top of being ugly, but it's an easy way |
|
268 // of having it work to continue working on the rest of the module |
|
269 // hopefully bypassing Python entirely pretty soon. |
|
270 def __iter__(&self) -> PyResult<PyObject> { |
|
271 let dict = PyDict::new(py); |
|
272 |
|
273 for (key, value) in self.dirs_map(py).borrow().iter() { |
|
274 dict.set_item( |
|
275 py, |
|
276 PyBytes::new(py, &key[..]), |
|
277 value.to_py_object(py), |
|
278 )?; |
|
279 } |
|
280 |
|
281 let locals = PyDict::new(py); |
|
282 locals.set_item(py, "obj", dict)?; |
|
283 |
|
284 py.eval("iter(obj)", None, Some(&locals)) |
|
285 } |
|
286 |
|
287 def __contains__(&self, item: PyObject) -> PyResult<bool> { |
|
288 Ok(self |
|
289 .dirs_map(py) |
|
290 .borrow() |
|
291 .contains_key(item.extract::<PyBytes>(py)?.data(py).as_ref())) |
|
292 } |
|
293 }); |
|
294 |
|
295 /// Create the module, with `__package__` given from parent |
203 /// Create the module, with `__package__` given from parent |
296 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
204 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
297 let dotted_name = &format!("{}.dirstate", package); |
205 let dotted_name = &format!("{}.dirstate", package); |
298 let m = PyModule::new(py, dotted_name)?; |
206 let m = PyModule::new(py, dotted_name)?; |
299 |
207 |