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 use cpython::{PyDict, PyModule, PyResult, Python}; |
10 use cindex::Index; |
|
11 use cpython::{ |
|
12 ObjectProtocol, PyClone, PyDict, PyModule, PyObject, PyResult, Python, |
|
13 }; |
|
14 use exceptions::GraphError; |
|
15 use hg; |
|
16 use hg::AncestorsIterator as CoreIterator; |
|
17 use hg::Revision; |
|
18 use std::cell::RefCell; |
|
19 |
|
20 /// Utility function to convert a Python iterable into a Vec<Revision> |
|
21 /// |
|
22 /// We need this to feed to AncestorIterators constructors because |
|
23 /// a PyErr can arise at each step of iteration, whereas our inner objects |
|
24 /// expect iterables over Revision, not over some Result<Revision, PyErr> |
|
25 fn reviter_to_revvec(py: Python, revs: PyObject) -> PyResult<Vec<Revision>> { |
|
26 revs.iter(py)? |
|
27 .map(|r| r.and_then(|o| o.extract::<Revision>(py))) |
|
28 .collect() |
|
29 } |
|
30 |
|
31 py_class!(class AncestorsIterator |py| { |
|
32 // TODO RW lock ? |
|
33 data inner: RefCell<Box<CoreIterator<Index>>>; |
|
34 |
|
35 def __next__(&self) -> PyResult<Option<Revision>> { |
|
36 match self.inner(py).borrow_mut().next() { |
|
37 Some(Err(e)) => Err(GraphError::pynew(py, e)), |
|
38 None => Ok(None), |
|
39 Some(Ok(r)) => Ok(Some(r)), |
|
40 } |
|
41 } |
|
42 |
|
43 def __contains__(&self, rev: Revision) -> PyResult<bool> { |
|
44 self.inner(py).borrow_mut().contains(rev).map_err(|e| GraphError::pynew(py, e)) |
|
45 } |
|
46 |
|
47 def __iter__(&self) -> PyResult<Self> { |
|
48 Ok(self.clone_ref(py)) |
|
49 } |
|
50 |
|
51 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision, |
|
52 inclusive: bool) -> PyResult<AncestorsIterator> { |
|
53 let initvec = reviter_to_revvec(py, initrevs)?; |
|
54 let ait = match hg::AncestorsIterator::new(Index::new(py, index)?, |
|
55 initvec, stoprev, |
|
56 inclusive) { |
|
57 Ok(ait) => ait, |
|
58 Err(e) => { |
|
59 return Err(GraphError::pynew(py, e)); |
|
60 } |
|
61 }; |
|
62 AncestorsIterator::from_inner(py, ait) |
|
63 } |
|
64 |
|
65 }); |
|
66 |
|
67 impl AncestorsIterator { |
|
68 pub fn from_inner(py: Python, ait: CoreIterator<Index>) -> PyResult<Self> { |
|
69 Self::create_instance(py, RefCell::new(Box::new(ait))) |
|
70 } |
|
71 } |
11 |
72 |
12 /// Create the module, with __package__ given from parent |
73 /// Create the module, with __package__ given from parent |
13 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
74 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
14 let dotted_name = &format!("{}.ancestor", package); |
75 let dotted_name = &format!("{}.ancestor", package); |
15 let m = PyModule::new(py, dotted_name)?; |
76 let m = PyModule::new(py, dotted_name)?; |
17 m.add( |
78 m.add( |
18 py, |
79 py, |
19 "__doc__", |
80 "__doc__", |
20 "Generic DAG ancestor algorithms - Rust implementation", |
81 "Generic DAG ancestor algorithms - Rust implementation", |
21 )?; |
82 )?; |
|
83 m.add_class::<AncestorsIterator>(py)?; |
22 |
84 |
23 let sys = PyModule::import(py, "sys")?; |
85 let sys = PyModule::import(py, "sys")?; |
24 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
86 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
25 sys_modules.set_item(py, dotted_name, &m)?; |
87 sys_modules.set_item(py, dotted_name, &m)?; |
26 // Example C code (see pyexpat.c and import.c) will "give away the |
88 // Example C code (see pyexpat.c and import.c) will "give away the |