rust/hg-cpython/src/dirstate.rs
changeset 42762 b3518b0baa47
parent 42609 326fdce22fb2
child 42763 760a7851e9ba
equal deleted inserted replaced
42761:4d20b1fe8a72 42762:b3518b0baa47
     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