##// END OF EJS Templates
rust-dirstate-status: update bridge for new rust version of `dirstate.status`...
Raphaël Gomès -
r44368:6a88ced3 default
parent child Browse files
Show More
@@ -1,173 +1,173 b''
1 1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5 mod ancestors;
6 6 pub mod dagops;
7 7 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
8 8 mod dirstate;
9 9 pub mod discovery;
10 10 pub mod testing; // unconditionally built, for use from integration tests
11 11 pub use dirstate::{
12 12 dirs_multiset::{DirsMultiset, DirsMultisetIter},
13 13 dirstate_map::DirstateMap,
14 14 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
15 status::status,
15 status::{status, StatusResult},
16 16 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
17 17 StateMap, StateMapIter,
18 18 };
19 19 mod filepatterns;
20 20 pub mod matchers;
21 21 pub mod utils;
22 22
23 23 use crate::utils::hg_path::HgPathBuf;
24 24 pub use filepatterns::{
25 25 build_single_regex, read_pattern_file, PatternSyntax, PatternTuple,
26 26 };
27 27 use std::collections::HashMap;
28 28 use twox_hash::RandomXxHashBuilder64;
29 29
30 30 /// Mercurial revision numbers
31 31 ///
32 32 /// As noted in revlog.c, revision numbers are actually encoded in
33 33 /// 4 bytes, and are liberally converted to ints, whence the i32
34 34 pub type Revision = i32;
35 35
36 36 /// Marker expressing the absence of a parent
37 37 ///
38 38 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
39 39 /// to be smaller that all existing revisions.
40 40 pub const NULL_REVISION: Revision = -1;
41 41
42 42 /// Same as `mercurial.node.wdirrev`
43 43 ///
44 44 /// This is also equal to `i32::max_value()`, but it's better to spell
45 45 /// it out explicitely, same as in `mercurial.node`
46 46 pub const WORKING_DIRECTORY_REVISION: Revision = 0x7fffffff;
47 47
48 48 /// The simplest expression of what we need of Mercurial DAGs.
49 49 pub trait Graph {
50 50 /// Return the two parents of the given `Revision`.
51 51 ///
52 52 /// Each of the parents can be independently `NULL_REVISION`
53 53 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError>;
54 54 }
55 55
56 56 pub type LineNumber = usize;
57 57
58 58 /// Rust's default hasher is too slow because it tries to prevent collision
59 59 /// attacks. We are not concerned about those: if an ill-minded person has
60 60 /// write access to your repository, you have other issues.
61 61 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
62 62
63 63 #[derive(Clone, Debug, PartialEq)]
64 64 pub enum GraphError {
65 65 ParentOutOfRange(Revision),
66 66 WorkingDirectoryUnsupported,
67 67 }
68 68
69 69 #[derive(Clone, Debug, PartialEq)]
70 70 pub enum DirstateParseError {
71 71 TooLittleData,
72 72 Overflow,
73 73 CorruptedEntry(String),
74 74 Damaged,
75 75 }
76 76
77 77 impl From<std::io::Error> for DirstateParseError {
78 78 fn from(e: std::io::Error) -> Self {
79 79 DirstateParseError::CorruptedEntry(e.to_string())
80 80 }
81 81 }
82 82
83 83 impl ToString for DirstateParseError {
84 84 fn to_string(&self) -> String {
85 85 use crate::DirstateParseError::*;
86 86 match self {
87 87 TooLittleData => "Too little data for dirstate.".to_string(),
88 88 Overflow => "Overflow in dirstate.".to_string(),
89 89 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
90 90 Damaged => "Dirstate appears to be damaged.".to_string(),
91 91 }
92 92 }
93 93 }
94 94
95 95 #[derive(Debug, PartialEq)]
96 96 pub enum DirstatePackError {
97 97 CorruptedEntry(String),
98 98 CorruptedParent,
99 99 BadSize(usize, usize),
100 100 }
101 101
102 102 impl From<std::io::Error> for DirstatePackError {
103 103 fn from(e: std::io::Error) -> Self {
104 104 DirstatePackError::CorruptedEntry(e.to_string())
105 105 }
106 106 }
107 107 #[derive(Debug, PartialEq)]
108 108 pub enum DirstateMapError {
109 109 PathNotFound(HgPathBuf),
110 110 EmptyPath,
111 111 ConsecutiveSlashes,
112 112 }
113 113
114 114 impl ToString for DirstateMapError {
115 115 fn to_string(&self) -> String {
116 116 use crate::DirstateMapError::*;
117 117 match self {
118 118 PathNotFound(_) => "expected a value, found none".to_string(),
119 119 EmptyPath => "Overflow in dirstate.".to_string(),
120 120 ConsecutiveSlashes => {
121 121 "found invalid consecutive slashes in path".to_string()
122 122 }
123 123 }
124 124 }
125 125 }
126 126
127 127 pub enum DirstateError {
128 128 Parse(DirstateParseError),
129 129 Pack(DirstatePackError),
130 130 Map(DirstateMapError),
131 131 IO(std::io::Error),
132 132 }
133 133
134 134 impl From<DirstateParseError> for DirstateError {
135 135 fn from(e: DirstateParseError) -> Self {
136 136 DirstateError::Parse(e)
137 137 }
138 138 }
139 139
140 140 impl From<DirstatePackError> for DirstateError {
141 141 fn from(e: DirstatePackError) -> Self {
142 142 DirstateError::Pack(e)
143 143 }
144 144 }
145 145
146 146 #[derive(Debug)]
147 147 pub enum PatternError {
148 148 UnsupportedSyntax(String),
149 149 }
150 150
151 151 #[derive(Debug)]
152 152 pub enum PatternFileError {
153 153 IO(std::io::Error),
154 154 Pattern(PatternError, LineNumber),
155 155 }
156 156
157 157 impl From<std::io::Error> for PatternFileError {
158 158 fn from(e: std::io::Error) -> Self {
159 159 PatternFileError::IO(e)
160 160 }
161 161 }
162 162
163 163 impl From<DirstateMapError> for DirstateError {
164 164 fn from(e: DirstateMapError) -> Self {
165 165 DirstateError::Map(e)
166 166 }
167 167 }
168 168
169 169 impl From<std::io::Error> for DirstateError {
170 170 fn from(e: std::io::Error) -> Self {
171 171 DirstateError::IO(e)
172 172 }
173 173 }
@@ -1,131 +1,132 b''
1 1 // dirstate.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate` module provided by the
9 9 //! `hg-core` package.
10 10 //!
11 11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12 12 mod copymap;
13 13 mod dirs_multiset;
14 14 mod dirstate_map;
15 15 mod status;
16 16 use crate::dirstate::{
17 17 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
18 18 };
19 19 use cpython::{
20 20 exc, PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence,
21 21 Python,
22 22 };
23 23 use hg::{
24 24 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParseError, EntryState,
25 25 StateMap,
26 26 };
27 27 use libc::{c_char, c_int};
28 28 use std::convert::TryFrom;
29 29
30 30 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
31 31 // for this type, and raises a Python `Exception` if the check does not pass.
32 32 // Because this type differs only in name from the regular Python tuple, it
33 33 // would be a good idea in the near future to remove it entirely to allow
34 34 // for a pure Python tuple of the same effective structure to be used,
35 35 // rendering this type and the capsule below useless.
36 36 py_capsule_fn!(
37 37 from mercurial.cext.parsers import make_dirstate_tuple_CAPI
38 38 as make_dirstate_tuple_capi
39 39 signature (
40 40 state: c_char,
41 41 mode: c_int,
42 42 size: c_int,
43 43 mtime: c_int,
44 44 ) -> *mut RawPyObject
45 45 );
46 46
47 47 pub fn make_dirstate_tuple(
48 48 py: Python,
49 49 entry: &DirstateEntry,
50 50 ) -> PyResult<PyObject> {
51 51 // might be silly to retrieve capsule function in hot loop
52 52 let make = make_dirstate_tuple_capi::retrieve(py)?;
53 53
54 54 let &DirstateEntry {
55 55 state,
56 56 mode,
57 57 size,
58 58 mtime,
59 59 } = entry;
60 60 // Explicitly go through u8 first, then cast to platform-specific `c_char`
61 61 // because Into<u8> has a specific implementation while `as c_char` would
62 62 // just do a naive enum cast.
63 63 let state_code: u8 = state.into();
64 64
65 65 let maybe_obj = unsafe {
66 66 let ptr = make(state_code as c_char, mode, size, mtime);
67 67 PyObject::from_owned_ptr_opt(py, ptr)
68 68 };
69 69 maybe_obj.ok_or_else(|| PyErr::fetch(py))
70 70 }
71 71
72 72 pub fn extract_dirstate(py: Python, dmap: &PyDict) -> Result<StateMap, PyErr> {
73 73 dmap.items(py)
74 74 .iter()
75 75 .map(|(filename, stats)| {
76 76 let stats = stats.extract::<PySequence>(py)?;
77 77 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
78 78 let state = EntryState::try_from(state.data(py)[0]).map_err(
79 79 |e: DirstateParseError| {
80 80 PyErr::new::<exc::ValueError, _>(py, e.to_string())
81 81 },
82 82 )?;
83 83 let mode = stats.get_item(py, 1)?.extract(py)?;
84 84 let size = stats.get_item(py, 2)?.extract(py)?;
85 85 let mtime = stats.get_item(py, 3)?.extract(py)?;
86 86 let filename = filename.extract::<PyBytes>(py)?;
87 87 let filename = filename.data(py);
88 88 Ok((
89 89 HgPathBuf::from(filename.to_owned()),
90 90 DirstateEntry {
91 91 state,
92 92 mode,
93 93 size,
94 94 mtime,
95 95 },
96 96 ))
97 97 })
98 98 .collect()
99 99 }
100 100
101 101 /// Create the module, with `__package__` given from parent
102 102 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
103 103 let dotted_name = &format!("{}.dirstate", package);
104 104 let m = PyModule::new(py, dotted_name)?;
105 105
106 106 m.add(py, "__package__", package)?;
107 107 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
108 108
109 109 m.add_class::<Dirs>(py)?;
110 110 m.add_class::<DirstateMap>(py)?;
111 111 m.add(
112 112 py,
113 113 "status",
114 114 py_fn!(
115 115 py,
116 116 status_wrapper(
117 117 dmap: DirstateMap,
118 118 root_dir: PyObject,
119 matcher: PyObject,
119 120 list_clean: bool,
120 121 last_normal_time: i64,
121 122 check_exec: bool
122 123 )
123 124 ),
124 125 )?;
125 126
126 127 let sys = PyModule::import(py, "sys")?;
127 128 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
128 129 sys_modules.set_item(py, dotted_name, &m)?;
129 130
130 131 Ok(m)
131 132 }
@@ -1,80 +1,129 b''
1 1 // status.rs
2 2 //
3 3 // Copyright 2019, Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::status` module provided by the
9 9 //! `hg-core` crate. From Python, this will be seen as
10 10 //! `rustext.dirstate.status`.
11 11
12 12 use crate::dirstate::DirstateMap;
13 13 use cpython::exc::ValueError;
14 14 use cpython::{
15 PyBytes, PyErr, PyList, PyObject, PyResult, Python, PythonObject,
16 ToPyObject,
15 ObjectProtocol, PyBytes, PyErr, PyList, PyObject, PyResult, PyTuple,
16 Python, PythonObject, ToPyObject,
17 17 };
18 use hg::utils::files::get_path_from_bytes;
19
20 use hg::matchers::AlwaysMatcher;
21 use hg::status;
22 use hg::utils::hg_path::HgPath;
18 use hg::utils::hg_path::HgPathBuf;
19 use hg::{
20 matchers::{AlwaysMatcher, FileMatcher},
21 status,
22 utils::{files::get_path_from_bytes, hg_path::HgPath},
23 StatusResult,
24 };
25 use std::borrow::Borrow;
23 26
24 27 /// This will be useless once trait impls for collection are added to `PyBytes`
25 28 /// upstream.
26 29 fn collect_pybytes_list<P: AsRef<HgPath>>(
27 30 py: Python,
28 31 collection: &[P],
29 32 ) -> PyList {
30 33 let list = PyList::new(py, &[]);
31 34
32 35 for (i, path) in collection.iter().enumerate() {
33 36 list.insert_item(
34 37 py,
35 38 i,
36 39 PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
37 40 )
38 41 }
39 42
40 43 list
41 44 }
42 45
43 46 pub fn status_wrapper(
44 47 py: Python,
45 48 dmap: DirstateMap,
49 matcher: PyObject,
46 50 root_dir: PyObject,
47 51 list_clean: bool,
48 52 last_normal_time: i64,
49 53 check_exec: bool,
50 54 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
51 55 let bytes = root_dir.extract::<PyBytes>(py)?;
52 56 let root_dir = get_path_from_bytes(bytes.data(py));
53 57
54 58 let dmap: DirstateMap = dmap.to_py_object(py);
55 59 let dmap = dmap.get_inner(py);
56 60
57 // TODO removed in the next patch to get the code to compile. This patch
58 // is part of a series and does not make real sense on its own.
61 match matcher.get_type(py).name(py).borrow() {
62 "alwaysmatcher" => {
59 63 let matcher = AlwaysMatcher;
60
61 64 let (lookup, status_res) = status(
62 65 &dmap,
63 66 &matcher,
64 67 &root_dir,
65 68 list_clean,
66 69 last_normal_time,
67 70 check_exec,
68 71 )
69 72 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
73 build_response(lookup, status_res, py)
74 }
75 "exactmatcher" => {
76 let files = matcher.call_method(
77 py,
78 "files",
79 PyTuple::new(py, &[]),
80 None,
81 )?;
82 let files: PyList = files.cast_into(py)?;
83 let files: PyResult<Vec<HgPathBuf>> = files
84 .iter(py)
85 .map(|f| {
86 Ok(HgPathBuf::from_bytes(
87 f.extract::<PyBytes>(py)?.data(py),
88 ))
89 })
90 .collect();
70 91
92 let files = files?;
93 let matcher = FileMatcher::new(&files)
94 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
95 let (lookup, status_res) = status(
96 &dmap,
97 &matcher,
98 &root_dir,
99 list_clean,
100 last_normal_time,
101 check_exec,
102 )
103 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
104 build_response(lookup, status_res, py)
105 }
106 e => {
107 return Err(PyErr::new::<ValueError, _>(
108 py,
109 format!("Unsupported matcher {}", e),
110 ));
111 }
112 }
113 }
114
115 fn build_response(
116 lookup: Vec<&HgPath>,
117 status_res: StatusResult,
118 py: Python,
119 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
71 120 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
72 121 let added = collect_pybytes_list(py, status_res.added.as_ref());
73 122 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
74 123 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
75 124 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
76 125 let lookup = collect_pybytes_list(py, lookup.as_ref());
77 126 let unknown = PyList::new(py, &[]);
78 127
79 128 Ok((lookup, modified, added, removed, deleted, unknown, clean))
80 129 }
General Comments 0
You need to be logged in to leave comments. Login now