##// END OF EJS Templates
rust-dirstate: create dirstate submodule in hg-cpython...
Raphaël Gomès -
r42991:b3518b0b default
parent child Browse files
Show More
@@ -9,22 +9,21 b''
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;
@@ -201,97 +200,6 b' fn pack_dirstate_wrapper('
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);
@@ -1,205 +1,22 b''
1 // dirstate.rs
1 // dirs_multiset.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate` module provided by the
8 //! Bindings for the `hg::dirstate::dirs_multiset` file 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 use std::cell::RefCell;
12
12
13 use cpython::{
13 use cpython::{
14 exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject,
14 exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyObject, PyResult,
15 PyResult, PySequence, PyTuple, Python, PythonObject, ToPyObject,
15 ToPyObject,
16 };
17 use hg::{
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirsIterable, DirsMultiset,
19 DirstateEntry, DirstateMapError, DirstatePackError, DirstateParents,
20 DirstateParseError, DirstateVec,
21 };
16 };
22 use libc::{c_char, c_int};
23 #[cfg(feature = "python27")]
24 use python27_sys::PyCapsule_Import;
25 #[cfg(feature = "python3")]
26 use python3_sys::PyCapsule_Import;
27 use std::cell::RefCell;
28 use std::collections::HashMap;
29 use std::ffi::CStr;
30 use std::mem::transmute;
31
32 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
33 /// for this type, and raises a Python `Exception` if the check does not pass.
34 /// Because this type differs only in name from the regular Python tuple, it
35 /// would be a good idea in the near future to remove it entirely to allow
36 /// for a pure Python tuple of the same effective structure to be used,
37 /// rendering this type and the capsule below useless.
38 type MakeDirstateTupleFn = extern "C" fn(
39 state: c_char,
40 mode: c_int,
41 size: c_int,
42 mtime: c_int,
43 ) -> PyObject;
44
45 /// This is largely a copy/paste from cindex.rs, pending the merge of a
46 /// `py_capsule_fn!` macro in the rust-cpython project:
47 /// https://github.com/dgrunwald/rust-cpython/pull/169
48 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
49 unsafe {
50 let caps_name = CStr::from_bytes_with_nul_unchecked(
51 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
52 );
53 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
54 if from_caps.is_null() {
55 return Err(PyErr::fetch(py));
56 }
57 Ok(transmute(from_caps))
58 }
59 }
60
61 fn parse_dirstate_wrapper(
62 py: Python,
63 dmap: PyDict,
64 copymap: PyDict,
65 st: PyBytes,
66 ) -> PyResult<PyTuple> {
67 match parse_dirstate(st.data(py)) {
68 Ok((parents, dirstate_vec, copies)) => {
69 for (filename, entry) in dirstate_vec {
70 dmap.set_item(
71 py,
72 PyBytes::new(py, &filename[..]),
73 decapsule_make_dirstate_tuple(py)?(
74 entry.state as c_char,
75 entry.mode,
76 entry.size,
77 entry.mtime,
78 ),
79 )?;
80 }
81 for CopyVecEntry { path, copy_path } in copies {
82 copymap.set_item(
83 py,
84 PyBytes::new(py, path),
85 PyBytes::new(py, copy_path),
86 )?;
87 }
88 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
89 .to_py_object(py))
90 }
91 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
92 py,
93 match e {
94 DirstateParseError::TooLittleData => {
95 "too little data for parents".to_string()
96 }
97 DirstateParseError::Overflow => {
98 "overflow in dirstate".to_string()
99 }
100 DirstateParseError::CorruptedEntry(e) => e,
101 },
102 )),
103 }
104 }
105
17
106 fn extract_dirstate_vec(
18 use crate::dirstate::extract_dirstate_vec;
107 py: Python,
19 use hg::{DirsIterable, DirsMultiset, DirstateMapError};
108 dmap: &PyDict,
109 ) -> Result<DirstateVec, PyErr> {
110 dmap.items(py)
111 .iter()
112 .map(|(filename, stats)| {
113 let stats = stats.extract::<PySequence>(py)?;
114 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
115 let state = state.data(py)[0] as i8;
116 let mode = stats.get_item(py, 1)?.extract(py)?;
117 let size = stats.get_item(py, 2)?.extract(py)?;
118 let mtime = stats.get_item(py, 3)?.extract(py)?;
119 let filename = filename.extract::<PyBytes>(py)?;
120 let filename = filename.data(py);
121 Ok((
122 filename.to_owned(),
123 DirstateEntry {
124 state,
125 mode,
126 size,
127 mtime,
128 },
129 ))
130 })
131 .collect()
132 }
133
134 fn pack_dirstate_wrapper(
135 py: Python,
136 dmap: PyDict,
137 copymap: PyDict,
138 pl: PyTuple,
139 now: PyInt,
140 ) -> PyResult<PyBytes> {
141 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
142 let p1: &[u8] = p1.data(py);
143 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
144 let p2: &[u8] = p2.data(py);
145
146 let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
147
148 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
149 .items(py)
150 .iter()
151 .map(|(key, value)| {
152 Ok((
153 key.extract::<PyBytes>(py)?.data(py).to_owned(),
154 value.extract::<PyBytes>(py)?.data(py).to_owned(),
155 ))
156 })
157 .collect();
158
159 match pack_dirstate(
160 &dirstate_vec,
161 &copies?,
162 DirstateParents { p1, p2 },
163 now.as_object().extract::<i32>(py)?,
164 ) {
165 Ok((packed, new_dirstate_vec)) => {
166 for (
167 filename,
168 DirstateEntry {
169 state,
170 mode,
171 size,
172 mtime,
173 },
174 ) in new_dirstate_vec
175 {
176 dmap.set_item(
177 py,
178 PyBytes::new(py, &filename[..]),
179 decapsule_make_dirstate_tuple(py)?(
180 state as c_char,
181 mode,
182 size,
183 mtime,
184 ),
185 )?;
186 }
187 Ok(PyBytes::new(py, &packed))
188 }
189 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
190 py,
191 match error {
192 DirstatePackError::CorruptedParent => {
193 "expected a 20-byte hash".to_string()
194 }
195 DirstatePackError::CorruptedEntry(e) => e,
196 DirstatePackError::BadSize(expected, actual) => {
197 format!("bad dirstate size: {} != {}", actual, expected)
198 }
199 },
200 )),
201 }
202 }
203
20
204 py_class!(pub class Dirs |py| {
21 py_class!(pub class Dirs |py| {
205 data dirs_map: RefCell<DirsMultiset>;
22 data dirs_map: RefCell<DirsMultiset>;
@@ -291,41 +108,3 b' py_class!(pub class Dirs |py| {'
291 .contains_key(item.extract::<PyBytes>(py)?.data(py).as_ref()))
108 .contains_key(item.extract::<PyBytes>(py)?.data(py).as_ref()))
292 }
109 }
293 });
110 });
294
295 /// Create the module, with `__package__` given from parent
296 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
297 let dotted_name = &format!("{}.dirstate", package);
298 let m = PyModule::new(py, dotted_name)?;
299
300 m.add(py, "__package__", package)?;
301 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
302 m.add(
303 py,
304 "parse_dirstate",
305 py_fn!(
306 py,
307 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
308 ),
309 )?;
310 m.add(
311 py,
312 "pack_dirstate",
313 py_fn!(
314 py,
315 pack_dirstate_wrapper(
316 dmap: PyDict,
317 copymap: PyDict,
318 pl: PyTuple,
319 now: PyInt
320 )
321 ),
322 )?;
323
324 m.add_class::<Dirs>(py)?;
325
326 let sys = PyModule::import(py, "sys")?;
327 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
328 sys_modules.set_item(py, dotted_name, &m)?;
329
330 Ok(m)
331 }
General Comments 0
You need to be logged in to leave comments. Login now