--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/dirstate/dirs_multiset.rs Mon Jul 08 18:01:39 2019 +0200
@@ -0,0 +1,110 @@
+// dirs_multiset.rs
+//
+// Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
+//! `hg-core` package.
+
+use std::cell::RefCell;
+
+use cpython::{
+ exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyObject, PyResult,
+ ToPyObject,
+};
+
+use crate::dirstate::extract_dirstate_vec;
+use hg::{DirsIterable, DirsMultiset, DirstateMapError};
+
+py_class!(pub class Dirs |py| {
+ data dirs_map: RefCell<DirsMultiset>;
+
+ // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
+ // a `list`)
+ def __new__(
+ _cls,
+ map: PyObject,
+ skip: Option<PyObject> = None
+ ) -> PyResult<Self> {
+ let mut skip_state: Option<i8> = None;
+ if let Some(skip) = skip {
+ skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8);
+ }
+ let dirs_map;
+
+ if let Ok(map) = map.cast_as::<PyDict>(py) {
+ let dirstate_vec = extract_dirstate_vec(py, &map)?;
+ dirs_map = DirsMultiset::new(
+ DirsIterable::Dirstate(dirstate_vec),
+ skip_state,
+ )
+ } else {
+ let map: Result<Vec<Vec<u8>>, PyErr> = map
+ .iter(py)?
+ .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
+ .collect();
+ dirs_map = DirsMultiset::new(
+ DirsIterable::Manifest(map?),
+ skip_state,
+ )
+ }
+
+ Self::create_instance(py, RefCell::new(dirs_map))
+ }
+
+ def addpath(&self, path: PyObject) -> PyResult<PyObject> {
+ self.dirs_map(py).borrow_mut().add_path(
+ path.extract::<PyBytes>(py)?.data(py),
+ );
+ Ok(py.None())
+ }
+
+ def delpath(&self, path: PyObject) -> PyResult<PyObject> {
+ self.dirs_map(py).borrow_mut().delete_path(
+ path.extract::<PyBytes>(py)?.data(py),
+ )
+ .and(Ok(py.None()))
+ .or_else(|e| {
+ match e {
+ DirstateMapError::PathNotFound(_p) => {
+ Err(PyErr::new::<exc::ValueError, _>(
+ py,
+ "expected a value, found none".to_string(),
+ ))
+ }
+ DirstateMapError::EmptyPath => {
+ Ok(py.None())
+ }
+ }
+ })
+ }
+
+ // This is really inefficient on top of being ugly, but it's an easy way
+ // of having it work to continue working on the rest of the module
+ // hopefully bypassing Python entirely pretty soon.
+ def __iter__(&self) -> PyResult<PyObject> {
+ let dict = PyDict::new(py);
+
+ for (key, value) in self.dirs_map(py).borrow().iter() {
+ dict.set_item(
+ py,
+ PyBytes::new(py, &key[..]),
+ value.to_py_object(py),
+ )?;
+ }
+
+ let locals = PyDict::new(py);
+ locals.set_item(py, "obj", dict)?;
+
+ py.eval("iter(obj)", None, Some(&locals))
+ }
+
+ def __contains__(&self, item: PyObject) -> PyResult<bool> {
+ Ok(self
+ .dirs_map(py)
+ .borrow()
+ .contains_key(item.extract::<PyBytes>(py)?.data(py).as_ref()))
+ }
+});