##// END OF EJS Templates
tests: allow the true command to be provided by coreutils...
tests: allow the true command to be provided by coreutils The `true` command is sometimes provided as a symbolic link to the `coreutils` single binary. This is the case on NixOS, on which the test was failing because the symbolic link fully resolves to the latter name, equally valid but not previously accepted by the test's golden output. Differential Revision: https://phab.mercurial-scm.org/D11740

File last commit:

r49079:269ff897 default
r49134:bc04a761 stable
Show More
status.rs
299 lines | 9.8 KiB | application/rls-services+xml | RustLexer
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 // status.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::status` module provided by the
Yuya Nishihara
rust-cpython: run cargo fmt
r43612 //! `hg-core` crate. From Python, this will be seen as
//! `rustext.dirstate.status`.
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 use crate::{dirstate::DirstateMap, exceptions::FallbackError};
Raphaël Gomès
rust-status: properly translate OSError to Python...
r46465 use cpython::exc::OSError;
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 use cpython::{
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 exc::ValueError, ObjectProtocol, PyBytes, PyErr, PyList, PyObject,
PyResult, PyTuple, Python, PythonObject, ToPyObject,
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 };
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 use hg::{
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 matchers::{AlwaysMatcher, FileMatcher, IncludeMatcher},
Simon Sapin
dirstate-tree: Make Rust DirstateMap bindings go through a trait object...
r47863 parse_pattern_syntax,
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 utils::{
files::{get_bytes_from_path, get_path_from_bytes},
hg_path::{HgPath, HgPathBuf},
},
BadMatch, DirstateStatus, IgnorePattern, PatternFileWarning, StatusError,
StatusOptions,
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 };
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 use std::borrow::Borrow;
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567
/// This will be useless once trait impls for collection are added to `PyBytes`
/// upstream.
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 fn collect_pybytes_list(
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 py: Python,
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 collection: &[impl AsRef<HgPath>],
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 ) -> PyList {
let list = PyList::new(py, &[]);
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 for path in collection.iter() {
list.append(
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 py,
PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
)
}
list
}
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 fn collect_bad_matches(
py: Python,
collection: &[(impl AsRef<HgPath>, BadMatch)],
) -> PyResult<PyList> {
let list = PyList::new(py, &[]);
let os = py.import("os")?;
let get_error_message = |code: i32| -> PyResult<_> {
os.call(
py,
"strerror",
PyTuple::new(py, &[code.to_py_object(py).into_object()]),
None,
)
};
for (path, bad_match) in collection.iter() {
let message = match bad_match {
BadMatch::OsError(code) => get_error_message(*code)?,
BadMatch::BadType(bad_type) => format!(
"unsupported file type (type is {})",
bad_type.to_string()
)
.to_py_object(py)
.into_object(),
};
list.append(
py,
(PyBytes::new(py, path.as_ref().as_bytes()), message)
.to_py_object(py)
.into_object(),
)
}
Ok(list)
}
fn handle_fallback(py: Python, err: StatusError) -> PyErr {
match err {
StatusError::Pattern(e) => {
Raphaël Gomès
rust-status: add trace-level logging for Rust status fallback for debugging...
r45062 let as_string = e.to_string();
log::trace!("Rust status fallback: `{}`", &as_string);
PyErr::new::<FallbackError, _>(py, &as_string)
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 }
Raphaël Gomès
rust-status: properly translate OSError to Python...
r46465 StatusError::IO(e) => PyErr::new::<OSError, _>(py, e.to_string()),
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 e => PyErr::new::<ValueError, _>(py, e.to_string()),
}
}
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 pub fn status_wrapper(
py: Python,
dmap: DirstateMap,
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 matcher: PyObject,
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 root_dir: PyObject,
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 ignore_files: PyList,
check_exec: bool,
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 last_normal_time: i64,
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 list_clean: bool,
list_ignored: bool,
list_unknown: bool,
Raphaël Gomès
rust-hg-cpython: update status bridge with the new `traversedir` support...
r45354 collect_traversed_dirs: bool,
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 ) -> PyResult<PyTuple> {
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 let bytes = root_dir.extract::<PyBytes>(py)?;
let root_dir = get_path_from_bytes(bytes.data(py));
let dmap: DirstateMap = dmap.to_py_object(py);
Simon Sapin
dirstate-tree: Give to `status()` mutable access to the `DirstateMap`...
r47882 let mut dmap = dmap.get_inner_mut(py);
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 let ignore_files: PyResult<Vec<_>> = ignore_files
.iter(py)
.map(|b| {
let file = b.extract::<PyBytes>(py)?;
Ok(get_path_from_bytes(file.data(py)).to_owned())
})
.collect();
let ignore_files = ignore_files?;
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 match matcher.get_type(py).name(py).borrow() {
"alwaysmatcher" => {
let matcher = AlwaysMatcher;
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 let (status_res, warnings) = dmap
Simon Sapin
dirstate-tree: Make Rust DirstateMap bindings go through a trait object...
r47863 .status(
&matcher,
root_dir.to_path_buf(),
ignore_files,
StatusOptions {
check_exec,
last_normal_time,
list_clean,
list_ignored,
list_unknown,
collect_traversed_dirs,
},
)
.map_err(|e| handle_fallback(py, e))?;
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 build_response(py, status_res, warnings)
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 }
"exactmatcher" => {
let files = matcher.call_method(
py,
"files",
PyTuple::new(py, &[]),
None,
)?;
let files: PyList = files.cast_into(py)?;
let files: PyResult<Vec<HgPathBuf>> = files
.iter(py)
.map(|f| {
Ok(HgPathBuf::from_bytes(
f.extract::<PyBytes>(py)?.data(py),
))
})
.collect();
Raphaël Gomès
rust-dirstate-status: add `walk_explicit` implementation, use `Matcher` trait...
r44367
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 let files = files?;
Raphaël Gomès
rust: start plugging the dirstate tree behind a feature gate...
r46185 let matcher = FileMatcher::new(files.as_ref())
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 let (status_res, warnings) = dmap
Simon Sapin
dirstate-tree: Make Rust DirstateMap bindings go through a trait object...
r47863 .status(
&matcher,
root_dir.to_path_buf(),
ignore_files,
StatusOptions {
check_exec,
last_normal_time,
list_clean,
list_ignored,
list_unknown,
collect_traversed_dirs,
},
)
.map_err(|e| handle_fallback(py, e))?;
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 build_response(py, status_res, warnings)
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 }
"includematcher" => {
// Get the patterns from Python even though most of them are
// redundant with those we will parse later on, as they include
// those passed from the command line.
let ignore_patterns: PyResult<Vec<_>> = matcher
.getattr(py, "_kindpats")?
.iter(py)?
.map(|k| {
let k = k?;
let syntax = parse_pattern_syntax(
&[
k.get_item(py, 0)?
.extract::<PyBytes>(py)?
.data(py),
&b":"[..],
]
.concat(),
)
.map_err(|e| {
handle_fallback(py, StatusError::Pattern(e))
})?;
let pattern = k.get_item(py, 1)?.extract::<PyBytes>(py)?;
let pattern = pattern.data(py);
let source = k.get_item(py, 2)?.extract::<PyBytes>(py)?;
let source = get_path_from_bytes(source.data(py));
let new = IgnorePattern::new(syntax, pattern, source);
Ok(new)
})
.collect();
let ignore_patterns = ignore_patterns?;
Simon Sapin
rust: Parse "subinclude"d files along the way, not later...
r48170 let matcher = IncludeMatcher::new(ignore_patterns)
.map_err(|e| handle_fallback(py, e.into()))?;
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 let (status_res, warnings) = dmap
Simon Sapin
dirstate-tree: Make Rust DirstateMap bindings go through a trait object...
r47863 .status(
&matcher,
root_dir.to_path_buf(),
ignore_files,
StatusOptions {
check_exec,
last_normal_time,
list_clean,
list_ignored,
list_unknown,
collect_traversed_dirs,
},
)
.map_err(|e| handle_fallback(py, e))?;
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016
Simon Sapin
rust: Parse "subinclude"d files along the way, not later...
r48170 build_response(py, status_res, warnings)
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 }
Raphaël Gomès
rust: do a clippy pass...
r45500 e => Err(PyErr::new::<ValueError, _>(
py,
format!("Unsupported matcher {}", e),
)),
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 }
}
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567
Raphaël Gomès
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
r44368 fn build_response(
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 py: Python,
Raphaël Gomès
rust-status: rename `StatusResult` to `DirstateStatus`...
r45012 status_res: DirstateStatus,
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 warnings: Vec<PatternFileWarning>,
) -> PyResult<PyTuple> {
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
let added = collect_pybytes_list(py, status_res.added.as_ref());
let removed = collect_pybytes_list(py, status_res.removed.as_ref());
let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
let clean = collect_pybytes_list(py, status_res.clean.as_ref());
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 let ignored = collect_pybytes_list(py, status_res.ignored.as_ref());
let unknown = collect_pybytes_list(py, status_res.unknown.as_ref());
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 let unsure = collect_pybytes_list(py, status_res.unsure.as_ref());
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 let bad = collect_bad_matches(py, status_res.bad.as_ref())?;
Raphaël Gomès
rust-hg-cpython: update status bridge with the new `traversedir` support...
r45354 let traversed = collect_pybytes_list(py, status_res.traversed.as_ref());
Simon Sapin
dirstate-v2: Write .hg/dirstate back to disk on directory cache changes...
r48139 let dirty = status_res.dirty.to_py_object(py);
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 let py_warnings = PyList::new(py, &[]);
for warning in warnings.iter() {
// We use duck-typing on the Python side for dispatch, good enough for
// now.
match warning {
PatternFileWarning::InvalidSyntax(file, syn) => {
py_warnings.append(
py,
(
PyBytes::new(py, &get_bytes_from_path(&file)),
PyBytes::new(py, syn),
)
.to_py_object(py)
.into_object(),
);
}
PatternFileWarning::NoSuchFile(file) => py_warnings.append(
py,
PyBytes::new(py, &get_bytes_from_path(&file)).into_object(),
),
}
}
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 Ok(PyTuple::new(
py,
&[
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 unsure.into_object(),
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 modified.into_object(),
added.into_object(),
removed.into_object(),
deleted.into_object(),
clean.into_object(),
ignored.into_object(),
unknown.into_object(),
py_warnings.into_object(),
bad.into_object(),
Raphaël Gomès
rust-hg-cpython: update status bridge with the new `traversedir` support...
r45354 traversed.into_object(),
Simon Sapin
dirstate-v2: Write .hg/dirstate back to disk on directory cache changes...
r48139 dirty.into_object(),
Raphaël Gomès
rust-status: update rust-cpython bridge to account for the changes in core...
r45016 ][..],
))
Raphaël Gomès
rust-dirstate-status: rust-cpython bindings for `dirstate.status`...
r43567 }