|
|
// dirstate.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` module provided by the
|
|
|
//! `hg-core` package.
|
|
|
//!
|
|
|
//! From Python, this will be seen as `mercurial.rustext.dirstate`
|
|
|
mod copymap;
|
|
|
mod dirs_multiset;
|
|
|
mod dirstate_map;
|
|
|
mod dispatch;
|
|
|
mod non_normal_entries;
|
|
|
mod owning;
|
|
|
mod status;
|
|
|
use crate::{
|
|
|
dirstate::{
|
|
|
dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
|
|
|
},
|
|
|
exceptions,
|
|
|
};
|
|
|
use cpython::{
|
|
|
exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
|
|
|
PySequence, Python,
|
|
|
};
|
|
|
use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
|
|
|
use hg::{utils::hg_path::HgPathBuf, DirstateEntry, EntryState, StateMap};
|
|
|
use libc::{c_char, c_int};
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
|
// C code uses a custom `dirstate_tuple` type, checks in multiple instances
|
|
|
// for this type, and raises a Python `Exception` if the check does not pass.
|
|
|
// Because this type differs only in name from the regular Python tuple, it
|
|
|
// would be a good idea in the near future to remove it entirely to allow
|
|
|
// for a pure Python tuple of the same effective structure to be used,
|
|
|
// rendering this type and the capsule below useless.
|
|
|
py_capsule_fn!(
|
|
|
from mercurial.cext.parsers import make_dirstate_item_CAPI
|
|
|
as make_dirstate_item_capi
|
|
|
signature (
|
|
|
state: c_char,
|
|
|
mode: c_int,
|
|
|
size: c_int,
|
|
|
mtime: c_int,
|
|
|
) -> *mut RawPyObject
|
|
|
);
|
|
|
|
|
|
pub fn make_dirstate_item(
|
|
|
py: Python,
|
|
|
entry: &DirstateEntry,
|
|
|
) -> PyResult<PyObject> {
|
|
|
let &DirstateEntry {
|
|
|
state,
|
|
|
mode,
|
|
|
size,
|
|
|
mtime,
|
|
|
} = entry;
|
|
|
// Explicitly go through u8 first, then cast to platform-specific `c_char`
|
|
|
// because Into<u8> has a specific implementation while `as c_char` would
|
|
|
// just do a naive enum cast.
|
|
|
let state_code: u8 = state.into();
|
|
|
make_dirstate_item_raw(py, state_code, mode, size, mtime)
|
|
|
}
|
|
|
|
|
|
pub fn make_dirstate_item_raw(
|
|
|
py: Python,
|
|
|
state: u8,
|
|
|
mode: i32,
|
|
|
size: i32,
|
|
|
mtime: i32,
|
|
|
) -> PyResult<PyObject> {
|
|
|
let make = make_dirstate_item_capi::retrieve(py)?;
|
|
|
let maybe_obj = unsafe {
|
|
|
let ptr = make(state as c_char, mode, size, mtime);
|
|
|
PyObject::from_owned_ptr_opt(py, ptr)
|
|
|
};
|
|
|
maybe_obj.ok_or_else(|| PyErr::fetch(py))
|
|
|
}
|
|
|
|
|
|
pub fn extract_dirstate(py: Python, dmap: &PyDict) -> Result<StateMap, PyErr> {
|
|
|
dmap.items(py)
|
|
|
.iter()
|
|
|
.map(|(filename, stats)| {
|
|
|
let stats = stats.extract::<PySequence>(py)?;
|
|
|
let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
|
|
|
let state =
|
|
|
EntryState::try_from(state.data(py)[0]).map_err(|e| {
|
|
|
PyErr::new::<exc::ValueError, _>(py, e.to_string())
|
|
|
})?;
|
|
|
let mode = stats.get_item(py, 1)?.extract(py)?;
|
|
|
let size = stats.get_item(py, 2)?.extract(py)?;
|
|
|
let mtime = stats.get_item(py, 3)?.extract(py)?;
|
|
|
let filename = filename.extract::<PyBytes>(py)?;
|
|
|
let filename = filename.data(py);
|
|
|
Ok((
|
|
|
HgPathBuf::from(filename.to_owned()),
|
|
|
DirstateEntry {
|
|
|
state,
|
|
|
mode,
|
|
|
size,
|
|
|
mtime,
|
|
|
},
|
|
|
))
|
|
|
})
|
|
|
.collect()
|
|
|
}
|
|
|
|
|
|
/// Create the module, with `__package__` given from parent
|
|
|
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
|
|
|
let dotted_name = &format!("{}.dirstate", package);
|
|
|
let m = PyModule::new(py, dotted_name)?;
|
|
|
|
|
|
env_logger::init();
|
|
|
|
|
|
m.add(py, "__package__", package)?;
|
|
|
m.add(py, "__doc__", "Dirstate - Rust implementation")?;
|
|
|
|
|
|
m.add(
|
|
|
py,
|
|
|
"FallbackError",
|
|
|
py.get_type::<exceptions::FallbackError>(),
|
|
|
)?;
|
|
|
m.add_class::<Dirs>(py)?;
|
|
|
m.add_class::<DirstateMap>(py)?;
|
|
|
m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
|
|
|
m.add(
|
|
|
py,
|
|
|
"status",
|
|
|
py_fn!(
|
|
|
py,
|
|
|
status_wrapper(
|
|
|
dmap: DirstateMap,
|
|
|
root_dir: PyObject,
|
|
|
matcher: PyObject,
|
|
|
ignorefiles: PyList,
|
|
|
check_exec: bool,
|
|
|
last_normal_time: i64,
|
|
|
list_clean: bool,
|
|
|
list_ignored: bool,
|
|
|
list_unknown: bool,
|
|
|
collect_traversed_dirs: bool
|
|
|
)
|
|
|
),
|
|
|
)?;
|
|
|
|
|
|
let sys = PyModule::import(py, "sys")?;
|
|
|
let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
|
|
|
sys_modules.set_item(py, dotted_name, &m)?;
|
|
|
|
|
|
Ok(m)
|
|
|
}
|
|
|
|