Show More
@@ -1,138 +1,137 | |||
|
1 | 1 | // ancestors.rs |
|
2 | 2 | // |
|
3 | 3 | // Copyright 2018 Georges Racinet <gracinet@anybox.fr> |
|
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::ancestors` module provided by the |
|
9 | 9 | //! `hg-core` crate. From Python, this will be seen as `rustext.ancestor` |
|
10 | 10 | //! and can be used as replacement for the the pure `ancestor` Python module. |
|
11 | 11 | //! |
|
12 | 12 | //! # Classes visible from Python: |
|
13 | 13 | //! - [`LazyAncestors`] is the Rust implementation of |
|
14 | 14 | //! `mercurial.ancestor.lazyancestors`. |
|
15 | 15 | //! The only difference is that it is instantiated with a C `parsers.index` |
|
16 | 16 | //! instance instead of a parents function. |
|
17 | 17 | //! |
|
18 | 18 | //! - [`AncestorsIterator`] is the Rust counterpart of the |
|
19 | 19 | //! `ancestor._lazyancestorsiter` Python generator. |
|
20 | 20 | //! From Python, instances of this should be mainly obtained by calling |
|
21 | 21 | //! `iter()` on a [`LazyAncestors`] instance. |
|
22 | 22 | //! |
|
23 | 23 | //! [`LazyAncestors`]: struct.LazyAncestors.html |
|
24 | 24 | //! [`AncestorsIterator`]: struct.AncestorsIterator.html |
|
25 | 25 | use cindex::Index; |
|
26 | 26 | use cpython::{ |
|
27 | 27 | ObjectProtocol, PyClone, PyDict, PyModule, PyObject, PyResult, Python, |
|
28 | 28 | }; |
|
29 | 29 | use exceptions::GraphError; |
|
30 | 30 | use hg::Revision; |
|
31 | 31 | use hg::{AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy}; |
|
32 | 32 | use std::cell::RefCell; |
|
33 | 33 | |
|
34 | 34 | /// Utility function to convert a Python iterable into a Vec<Revision> |
|
35 | 35 | /// |
|
36 | 36 | /// We need this to feed to `AncestorIterators` constructors because |
|
37 | 37 | /// a `PyErr` can arise at each step of iteration, whereas our inner objects |
|
38 | 38 | /// expect iterables over `Revision`, not over some `Result<Revision, PyErr>` |
|
39 | 39 | fn reviter_to_revvec(py: Python, revs: PyObject) -> PyResult<Vec<Revision>> { |
|
40 | 40 | revs.iter(py)? |
|
41 | 41 | .map(|r| r.and_then(|o| o.extract::<Revision>(py))) |
|
42 | 42 | .collect() |
|
43 | 43 | } |
|
44 | 44 | |
|
45 | 45 | py_class!(pub class AncestorsIterator |py| { |
|
46 | // TODO RW lock ? | |
|
47 | 46 | data inner: RefCell<Box<CoreIterator<Index>>>; |
|
48 | 47 | |
|
49 | 48 | def __next__(&self) -> PyResult<Option<Revision>> { |
|
50 | 49 | match self.inner(py).borrow_mut().next() { |
|
51 | 50 | Some(Err(e)) => Err(GraphError::pynew(py, e)), |
|
52 | 51 | None => Ok(None), |
|
53 | 52 | Some(Ok(r)) => Ok(Some(r)), |
|
54 | 53 | } |
|
55 | 54 | } |
|
56 | 55 | |
|
57 | 56 | def __contains__(&self, rev: Revision) -> PyResult<bool> { |
|
58 |
self.inner(py).borrow_mut().contains(rev) |
|
|
57 | self.inner(py).borrow_mut().contains(rev) | |
|
58 | .map_err(|e| GraphError::pynew(py, e)) | |
|
59 | 59 | } |
|
60 | 60 | |
|
61 | 61 | def __iter__(&self) -> PyResult<Self> { |
|
62 | 62 | Ok(self.clone_ref(py)) |
|
63 | 63 | } |
|
64 | 64 | |
|
65 | 65 | def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision, |
|
66 | 66 | inclusive: bool) -> PyResult<AncestorsIterator> { |
|
67 | 67 | let initvec = reviter_to_revvec(py, initrevs)?; |
|
68 |
let ait = |
|
|
69 | initvec, stoprev, | |
|
70 | inclusive) { | |
|
71 | Ok(ait) => ait, | |
|
72 | Err(e) => { | |
|
73 | return Err(GraphError::pynew(py, e)); | |
|
74 | } | |
|
75 | }; | |
|
68 | let ait = CoreIterator::new( | |
|
69 | Index::new(py, index)?, | |
|
70 | initvec, | |
|
71 | stoprev, | |
|
72 | inclusive, | |
|
73 | ) | |
|
74 | .map_err(|e| GraphError::pynew(py, e))?; | |
|
76 | 75 | AncestorsIterator::from_inner(py, ait) |
|
77 | 76 | } |
|
78 | 77 | |
|
79 | 78 | }); |
|
80 | 79 | |
|
81 | 80 | impl AncestorsIterator { |
|
82 | 81 | pub fn from_inner(py: Python, ait: CoreIterator<Index>) -> PyResult<Self> { |
|
83 | 82 | Self::create_instance(py, RefCell::new(Box::new(ait))) |
|
84 | 83 | } |
|
85 | 84 | } |
|
86 | 85 | |
|
87 | 86 | py_class!(pub class LazyAncestors |py| { |
|
88 | 87 | data inner: RefCell<Box<CoreLazy<Index>>>; |
|
89 | 88 | |
|
90 | 89 | def __contains__(&self, rev: Revision) -> PyResult<bool> { |
|
91 | 90 | self.inner(py) |
|
92 | 91 | .borrow_mut() |
|
93 | 92 | .contains(rev) |
|
94 | 93 | .map_err(|e| GraphError::pynew(py, e)) |
|
95 | 94 | } |
|
96 | 95 | |
|
97 | 96 | def __iter__(&self) -> PyResult<AncestorsIterator> { |
|
98 | 97 | AncestorsIterator::from_inner(py, self.inner(py).borrow().iter()) |
|
99 | 98 | } |
|
100 | 99 | |
|
101 | 100 | def __bool__(&self) -> PyResult<bool> { |
|
102 | 101 | Ok(!self.inner(py).borrow().is_empty()) |
|
103 | 102 | } |
|
104 | 103 | |
|
105 | 104 | def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision, |
|
106 | 105 | inclusive: bool) -> PyResult<Self> { |
|
107 | 106 | let initvec = reviter_to_revvec(py, initrevs)?; |
|
108 | 107 | |
|
109 | 108 | let lazy = |
|
110 | 109 | CoreLazy::new(Index::new(py, index)?, initvec, stoprev, inclusive) |
|
111 | 110 | .map_err(|e| GraphError::pynew(py, e))?; |
|
112 | 111 | |
|
113 | 112 | Self::create_instance(py, RefCell::new(Box::new(lazy))) |
|
114 | 113 | } |
|
115 | 114 | |
|
116 | 115 | }); |
|
117 | 116 | |
|
118 | 117 | /// Create the module, with `__package__` given from parent |
|
119 | 118 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
|
120 | 119 | let dotted_name = &format!("{}.ancestor", package); |
|
121 | 120 | let m = PyModule::new(py, dotted_name)?; |
|
122 | 121 | m.add(py, "__package__", package)?; |
|
123 | 122 | m.add( |
|
124 | 123 | py, |
|
125 | 124 | "__doc__", |
|
126 | 125 | "Generic DAG ancestor algorithms - Rust implementation", |
|
127 | 126 | )?; |
|
128 | 127 | m.add_class::<AncestorsIterator>(py)?; |
|
129 | 128 | m.add_class::<LazyAncestors>(py)?; |
|
130 | 129 | |
|
131 | 130 | let sys = PyModule::import(py, "sys")?; |
|
132 | 131 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
|
133 | 132 | sys_modules.set_item(py, dotted_name, &m)?; |
|
134 | 133 | // Example C code (see pyexpat.c and import.c) will "give away the |
|
135 | 134 | // reference", but we won't because it will be consumed once the |
|
136 | 135 | // Rust PyObject is dropped. |
|
137 | 136 | Ok(m) |
|
138 | 137 | } |
General Comments 0
You need to be logged in to leave comments.
Login now