##// END OF EJS Templates
rust-dirstate: rust-cpython bridge for dirstatemap...
Raphaël Gomès -
r42999:4e8f5044 default
parent child Browse files
Show More
@@ -0,0 +1,116 b''
1 // copymap.rs
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
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.
7
8 //! Bindings for `hg::dirstate::dirstate_map::CopyMap` provided by the
9 //! `hg-core` package.
10
11 use cpython::{PyBytes, PyClone, PyDict, PyObject, PyResult, Python};
12 use std::cell::RefCell;
13
14 use crate::dirstate::dirstate_map::{DirstateMap, DirstateMapLeakedRef};
15
16 py_class!(pub class CopyMap |py| {
17 data dirstate_map: DirstateMap;
18
19 def __getitem__(&self, key: PyObject) -> PyResult<PyBytes> {
20 (*self.dirstate_map(py)).copymapgetitem(py, key)
21 }
22
23 def __len__(&self) -> PyResult<usize> {
24 self.dirstate_map(py).copymaplen(py)
25 }
26
27 def __contains__(&self, key: PyObject) -> PyResult<bool> {
28 self.dirstate_map(py).copymapcontains(py, key)
29 }
30
31 def get(
32 &self,
33 key: PyObject,
34 default: Option<PyObject> = None
35 ) -> PyResult<Option<PyObject>> {
36 self.dirstate_map(py).copymapget(py, key, default)
37 }
38
39 def pop(
40 &self,
41 key: PyObject,
42 default: Option<PyObject> = None
43 ) -> PyResult<Option<PyObject>> {
44 self.dirstate_map(py).copymappop(py, key, default)
45 }
46
47 def __iter__(&self) -> PyResult<CopyMapKeysIterator> {
48 self.dirstate_map(py).copymapiter(py)
49 }
50
51 // Python's `dict()` builtin works with either a subclass of dict
52 // or an abstract mapping. Said mapping needs to implement `__getitem__`
53 // and `keys`.
54 def keys(&self) -> PyResult<CopyMapKeysIterator> {
55 self.dirstate_map(py).copymapiter(py)
56 }
57
58 def items(&self) -> PyResult<CopyMapItemsIterator> {
59 self.dirstate_map(py).copymapitemsiter(py)
60 }
61
62 def iteritems(&self) -> PyResult<CopyMapItemsIterator> {
63 self.dirstate_map(py).copymapitemsiter(py)
64 }
65
66 def __setitem__(
67 &self,
68 key: PyObject,
69 item: PyObject
70 ) -> PyResult<()> {
71 self.dirstate_map(py).copymapsetitem(py, key, item)?;
72 Ok(())
73 }
74
75 def copy(&self) -> PyResult<PyDict> {
76 self.dirstate_map(py).copymapcopy(py)
77 }
78
79 });
80
81 impl CopyMap {
82 pub fn from_inner(py: Python, dm: DirstateMap) -> PyResult<Self> {
83 Self::create_instance(py, dm)
84 }
85 fn translate_key(
86 py: Python,
87 res: (&Vec<u8>, &Vec<u8>),
88 ) -> PyResult<Option<PyBytes>> {
89 Ok(Some(PyBytes::new(py, res.0)))
90 }
91 fn translate_key_value(
92 py: Python,
93 res: (&Vec<u8>, &Vec<u8>),
94 ) -> PyResult<Option<(PyBytes, PyBytes)>> {
95 let (k, v) = res;
96 Ok(Some((PyBytes::new(py, k), PyBytes::new(py, v))))
97 }
98 }
99
100 py_shared_mapping_iterator!(
101 CopyMapKeysIterator,
102 DirstateMapLeakedRef,
103 Vec<u8>,
104 Vec<u8>,
105 CopyMap::translate_key,
106 Option<PyBytes>
107 );
108
109 py_shared_mapping_iterator!(
110 CopyMapItemsIterator,
111 DirstateMapLeakedRef,
112 Vec<u8>,
113 Vec<u8>,
114 CopyMap::translate_key_value,
115 Option<(PyBytes, PyBytes)>
116 );
This diff has been collapsed as it changes many lines, (508 lines changed) Show them Hide them
@@ -0,0 +1,508 b''
1 // dirstate_map.rs
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
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.
7
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
10
11 use std::cell::RefCell;
12 use std::convert::TryInto;
13 use std::time::Duration;
14
15 use cpython::{
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
18 };
19 use libc::c_char;
20
21 use crate::{
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
24 ref_sharing::PySharedState,
25 };
26 use hg::{
27 utils::copy_into_array, DirsIterable, DirsMultiset, DirstateEntry,
28 DirstateMap as RustDirstateMap, DirstateParents, DirstateParseError,
29 EntryState,
30 };
31
32 // TODO
33 // This object needs to share references to multiple members of its Rust
34 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
35 // Right now `CopyMap` is done, but it needs to have an explicit reference
36 // to `RustDirstateMap` which itself needs to have an encapsulation for
37 // every method in `CopyMap` (copymapcopy, etc.).
38 // This is ugly and hard to maintain.
39 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
40 // `py_class!` is already implemented and does not mention
41 // `RustDirstateMap`, rightfully so.
42 // All attributes also have to have a separate refcount data attribute for
43 // leaks, with all methods that go along for reference sharing.
44 py_class!(pub class DirstateMap |py| {
45 data inner: RefCell<RustDirstateMap>;
46 data py_shared_state: PySharedState;
47
48 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
49 let inner = RustDirstateMap::default();
50 Self::create_instance(
51 py,
52 RefCell::new(inner),
53 PySharedState::default()
54 )
55 }
56
57 def clear(&self) -> PyResult<PyObject> {
58 self.borrow_mut(py)?.clear();
59 Ok(py.None())
60 }
61
62 def get(
63 &self,
64 key: PyObject,
65 default: Option<PyObject> = None
66 ) -> PyResult<Option<PyObject>> {
67 let key = key.extract::<PyBytes>(py)?;
68 match self.inner(py).borrow().get(key.data(py)) {
69 Some(entry) => {
70 // Explicitly go through u8 first, then cast to
71 // platform-specific `c_char`.
72 let state: u8 = entry.state.into();
73 Ok(Some(decapsule_make_dirstate_tuple(py)?(
74 state as c_char,
75 entry.mode,
76 entry.size,
77 entry.mtime,
78 )))
79 },
80 None => Ok(default)
81 }
82 }
83
84 def addfile(
85 &self,
86 f: PyObject,
87 oldstate: PyObject,
88 state: PyObject,
89 mode: PyObject,
90 size: PyObject,
91 mtime: PyObject
92 ) -> PyResult<PyObject> {
93 self.borrow_mut(py)?.add_file(
94 f.extract::<PyBytes>(py)?.data(py),
95 oldstate.extract::<PyBytes>(py)?.data(py)[0]
96 .try_into()
97 .map_err(|e: DirstateParseError| {
98 PyErr::new::<exc::ValueError, _>(py, e.to_string())
99 })?,
100 DirstateEntry {
101 state: state.extract::<PyBytes>(py)?.data(py)[0]
102 .try_into()
103 .map_err(|e: DirstateParseError| {
104 PyErr::new::<exc::ValueError, _>(py, e.to_string())
105 })?,
106 mode: mode.extract(py)?,
107 size: size.extract(py)?,
108 mtime: mtime.extract(py)?,
109 },
110 );
111 Ok(py.None())
112 }
113
114 def removefile(
115 &self,
116 f: PyObject,
117 oldstate: PyObject,
118 size: PyObject
119 ) -> PyResult<PyObject> {
120 self.borrow_mut(py)?
121 .remove_file(
122 f.extract::<PyBytes>(py)?.data(py),
123 oldstate.extract::<PyBytes>(py)?.data(py)[0]
124 .try_into()
125 .map_err(|e: DirstateParseError| {
126 PyErr::new::<exc::ValueError, _>(py, e.to_string())
127 })?,
128 size.extract(py)?,
129 )
130 .or_else(|_| {
131 Err(PyErr::new::<exc::OSError, _>(
132 py,
133 "Dirstate error".to_string(),
134 ))
135 })?;
136 Ok(py.None())
137 }
138
139 def dropfile(
140 &self,
141 f: PyObject,
142 oldstate: PyObject
143 ) -> PyResult<PyBool> {
144 self.borrow_mut(py)?
145 .drop_file(
146 f.extract::<PyBytes>(py)?.data(py),
147 oldstate.extract::<PyBytes>(py)?.data(py)[0]
148 .try_into()
149 .map_err(|e: DirstateParseError| {
150 PyErr::new::<exc::ValueError, _>(py, e.to_string())
151 })?,
152 )
153 .and_then(|b| Ok(b.to_py_object(py)))
154 .or_else(|_| {
155 Err(PyErr::new::<exc::OSError, _>(
156 py,
157 "Dirstate error".to_string(),
158 ))
159 })
160 }
161
162 def clearambiguoustimes(
163 &self,
164 files: PyObject,
165 now: PyObject
166 ) -> PyResult<PyObject> {
167 let files: PyResult<Vec<Vec<u8>>> = files
168 .iter(py)?
169 .map(|filename| {
170 Ok(filename?.extract::<PyBytes>(py)?.data(py).to_owned())
171 })
172 .collect();
173 self.inner(py)
174 .borrow_mut()
175 .clear_ambiguous_times(files?, now.extract(py)?);
176 Ok(py.None())
177 }
178
179 // TODO share the reference
180 def nonnormalentries(&self) -> PyResult<PyObject> {
181 let (non_normal, other_parent) =
182 self.inner(py).borrow().non_normal_other_parent_entries();
183
184 let locals = PyDict::new(py);
185 locals.set_item(
186 py,
187 "non_normal",
188 non_normal
189 .iter()
190 .map(|v| PyBytes::new(py, &v))
191 .collect::<Vec<PyBytes>>()
192 .to_py_object(py),
193 )?;
194 locals.set_item(
195 py,
196 "other_parent",
197 other_parent
198 .iter()
199 .map(|v| PyBytes::new(py, &v))
200 .collect::<Vec<PyBytes>>()
201 .to_py_object(py),
202 )?;
203
204 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
205 }
206
207 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
208 let d = d.extract::<PyBytes>(py)?;
209 Ok(self
210 .inner(py)
211 .borrow_mut()
212 .has_tracked_dir(d.data(py))
213 .to_py_object(py))
214 }
215
216 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
217 let d = d.extract::<PyBytes>(py)?;
218 Ok(self
219 .inner(py)
220 .borrow_mut()
221 .has_dir(d.data(py))
222 .to_py_object(py))
223 }
224
225 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
226 self.inner(py)
227 .borrow_mut()
228 .parents(st.extract::<PyBytes>(py)?.data(py))
229 .and_then(|d| {
230 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
231 .to_py_object(py))
232 })
233 .or_else(|_| {
234 Err(PyErr::new::<exc::OSError, _>(
235 py,
236 "Dirstate error".to_string(),
237 ))
238 })
239 }
240
241 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
242 let p1 = copy_into_array(p1.extract::<PyBytes>(py)?.data(py));
243 let p2 = copy_into_array(p2.extract::<PyBytes>(py)?.data(py));
244
245 self.inner(py)
246 .borrow_mut()
247 .set_parents(DirstateParents { p1, p2 });
248 Ok(py.None())
249 }
250
251 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
252 match self
253 .inner(py)
254 .borrow_mut()
255 .read(st.extract::<PyBytes>(py)?.data(py))
256 {
257 Ok(Some(parents)) => Ok(Some(
258 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
259 .to_py_object(py)
260 .into_object(),
261 )),
262 Ok(None) => Ok(Some(py.None())),
263 Err(_) => Err(PyErr::new::<exc::OSError, _>(
264 py,
265 "Dirstate error".to_string(),
266 )),
267 }
268 }
269 def write(
270 &self,
271 p1: PyObject,
272 p2: PyObject,
273 now: PyObject
274 ) -> PyResult<PyBytes> {
275 let now = Duration::new(now.extract(py)?, 0);
276 let parents = DirstateParents {
277 p1: copy_into_array(p1.extract::<PyBytes>(py)?.data(py)),
278 p2: copy_into_array(p2.extract::<PyBytes>(py)?.data(py)),
279 };
280
281 match self.borrow_mut(py)?.pack(parents, now) {
282 Ok(packed) => Ok(PyBytes::new(py, &packed)),
283 Err(_) => Err(PyErr::new::<exc::OSError, _>(
284 py,
285 "Dirstate error".to_string(),
286 )),
287 }
288 }
289
290 def filefoldmapasdict(&self) -> PyResult<PyDict> {
291 let dict = PyDict::new(py);
292 for (key, value) in
293 self.borrow_mut(py)?.build_file_fold_map().iter()
294 {
295 dict.set_item(py, key, value)?;
296 }
297 Ok(dict)
298 }
299
300 def __len__(&self) -> PyResult<usize> {
301 Ok(self.inner(py).borrow().len())
302 }
303
304 def __contains__(&self, key: PyObject) -> PyResult<bool> {
305 let key = key.extract::<PyBytes>(py)?;
306 Ok(self.inner(py).borrow().contains_key(key.data(py)))
307 }
308
309 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
310 let key = key.extract::<PyBytes>(py)?;
311 let key = key.data(py);
312 match self.inner(py).borrow().get(key) {
313 Some(entry) => {
314 // Explicitly go through u8 first, then cast to
315 // platform-specific `c_char`.
316 let state: u8 = entry.state.into();
317 Ok(decapsule_make_dirstate_tuple(py)?(
318 state as c_char,
319 entry.mode,
320 entry.size,
321 entry.mtime,
322 ))
323 },
324 None => Err(PyErr::new::<exc::KeyError, _>(
325 py,
326 String::from_utf8_lossy(key),
327 )),
328 }
329 }
330
331 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
332 DirstateMapKeysIterator::from_inner(
333 py,
334 Some(DirstateMapLeakedRef::new(py, &self)),
335 Box::new(self.leak_immutable(py)?.iter()),
336 )
337 }
338
339 def items(&self) -> PyResult<DirstateMapItemsIterator> {
340 DirstateMapItemsIterator::from_inner(
341 py,
342 Some(DirstateMapLeakedRef::new(py, &self)),
343 Box::new(self.leak_immutable(py)?.iter()),
344 )
345 }
346
347 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
348 DirstateMapKeysIterator::from_inner(
349 py,
350 Some(DirstateMapLeakedRef::new(py, &self)),
351 Box::new(self.leak_immutable(py)?.iter()),
352 )
353 }
354
355 def getdirs(&self) -> PyResult<Dirs> {
356 // TODO don't copy, share the reference
357 self.inner(py).borrow_mut().set_dirs();
358 Dirs::from_inner(
359 py,
360 DirsMultiset::new(
361 DirsIterable::Dirstate(&self.inner(py).borrow()),
362 Some(EntryState::Removed),
363 ),
364 )
365 }
366 def getalldirs(&self) -> PyResult<Dirs> {
367 // TODO don't copy, share the reference
368 self.inner(py).borrow_mut().set_all_dirs();
369 Dirs::from_inner(
370 py,
371 DirsMultiset::new(
372 DirsIterable::Dirstate(&self.inner(py).borrow()),
373 None,
374 ),
375 )
376 }
377
378 // TODO all copymap* methods, see docstring above
379 def copymapcopy(&self) -> PyResult<PyDict> {
380 let dict = PyDict::new(py);
381 for (key, value) in self.inner(py).borrow().copy_map.iter() {
382 dict.set_item(py, PyBytes::new(py, key), PyBytes::new(py, value))?;
383 }
384 Ok(dict)
385 }
386
387 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
388 let key = key.extract::<PyBytes>(py)?;
389 match self.inner(py).borrow().copy_map.get(key.data(py)) {
390 Some(copy) => Ok(PyBytes::new(py, copy)),
391 None => Err(PyErr::new::<exc::KeyError, _>(
392 py,
393 String::from_utf8_lossy(key.data(py)),
394 )),
395 }
396 }
397 def copymap(&self) -> PyResult<CopyMap> {
398 CopyMap::from_inner(py, self.clone_ref(py))
399 }
400
401 def copymaplen(&self) -> PyResult<usize> {
402 Ok(self.inner(py).borrow().copy_map.len())
403 }
404 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
405 let key = key.extract::<PyBytes>(py)?;
406 Ok(self.inner(py).borrow().copy_map.contains_key(key.data(py)))
407 }
408 def copymapget(
409 &self,
410 key: PyObject,
411 default: Option<PyObject>
412 ) -> PyResult<Option<PyObject>> {
413 let key = key.extract::<PyBytes>(py)?;
414 match self.inner(py).borrow().copy_map.get(key.data(py)) {
415 Some(copy) => Ok(Some(PyBytes::new(py, copy).into_object())),
416 None => Ok(default),
417 }
418 }
419 def copymapsetitem(
420 &self,
421 key: PyObject,
422 value: PyObject
423 ) -> PyResult<PyObject> {
424 let key = key.extract::<PyBytes>(py)?;
425 let value = value.extract::<PyBytes>(py)?;
426 self.inner(py)
427 .borrow_mut()
428 .copy_map
429 .insert(key.data(py).to_vec(), value.data(py).to_vec());
430 Ok(py.None())
431 }
432 def copymappop(
433 &self,
434 key: PyObject,
435 default: Option<PyObject>
436 ) -> PyResult<Option<PyObject>> {
437 let key = key.extract::<PyBytes>(py)?;
438 match self.inner(py).borrow_mut().copy_map.remove(key.data(py)) {
439 Some(_) => Ok(None),
440 None => Ok(default),
441 }
442 }
443
444 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
445 CopyMapKeysIterator::from_inner(
446 py,
447 Some(DirstateMapLeakedRef::new(py, &self)),
448 Box::new(self.leak_immutable(py)?.copy_map.iter()),
449 )
450 }
451
452 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
453 CopyMapItemsIterator::from_inner(
454 py,
455 Some(DirstateMapLeakedRef::new(py, &self)),
456 Box::new(self.leak_immutable(py)?.copy_map.iter()),
457 )
458 }
459
460 });
461
462 impl DirstateMap {
463 fn translate_key(
464 py: Python,
465 res: (&Vec<u8>, &DirstateEntry),
466 ) -> PyResult<Option<PyBytes>> {
467 Ok(Some(PyBytes::new(py, res.0)))
468 }
469 fn translate_key_value(
470 py: Python,
471 res: (&Vec<u8>, &DirstateEntry),
472 ) -> PyResult<Option<(PyBytes, PyObject)>> {
473 let (f, entry) = res;
474
475 // Explicitly go through u8 first, then cast to
476 // platform-specific `c_char`.
477 let state: u8 = entry.state.into();
478 Ok(Some((
479 PyBytes::new(py, f),
480 decapsule_make_dirstate_tuple(py)?(
481 state as c_char,
482 entry.mode,
483 entry.size,
484 entry.mtime,
485 ),
486 )))
487 }
488 }
489
490 py_shared_ref!(DirstateMap, RustDirstateMap, inner, DirstateMapLeakedRef,);
491
492 py_shared_mapping_iterator!(
493 DirstateMapKeysIterator,
494 DirstateMapLeakedRef,
495 Vec<u8>,
496 DirstateEntry,
497 DirstateMap::translate_key,
498 Option<PyBytes>
499 );
500
501 py_shared_mapping_iterator!(
502 DirstateMapItemsIterator,
503 DirstateMapLeakedRef,
504 Vec<u8>,
505 DirstateEntry,
506 DirstateMap::translate_key_value,
507 Option<(PyBytes, PyObject)>
508 );
@@ -9,8 +9,10 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 mod copymap;
12 mod dirs_multiset;
13 mod dirs_multiset;
13 use crate::dirstate::dirs_multiset::Dirs;
14 mod dirstate_map;
15 use crate::dirstate::{dirs_multiset::Dirs, dirstate_map::DirstateMap};
14 use cpython::{
16 use cpython::{
15 exc, PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence,
17 exc, PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence,
16 Python,
18 Python,
@@ -94,6 +96,7 b' pub fn init_module(py: Python, package: '
94 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
96 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
95
97
96 m.add_class::<Dirs>(py)?;
98 m.add_class::<Dirs>(py)?;
99 m.add_class::<DirstateMap>(py)?;
97
100
98 let sys = PyModule::import(py, "sys")?;
101 let sys = PyModule::import(py, "sys")?;
99 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
102 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
General Comments 0
You need to be logged in to leave comments. Login now