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