Show More
@@ -5,12 +5,20 b'' | |||
|
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 |
use crate:: |
|
|
8 | use crate::{ | |
|
9 | cindex, | |
|
10 | utils::{node_from_py_bytes, node_from_py_object}, | |
|
11 | }; | |
|
9 | 12 | use cpython::{ |
|
10 | exc::ValueError, ObjectProtocol, PyClone, PyDict, PyErr, PyModule, | |
|
11 | PyObject, PyResult, PyTuple, Python, PythonObject, ToPyObject, | |
|
13 | exc::{IndexError, ValueError}, | |
|
14 | ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyModule, PyObject, | |
|
15 | PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject, | |
|
12 | 16 | }; |
|
13 | use hg::{nodemap::NodeMapError, NodeError, Revision}; | |
|
17 | use hg::{ | |
|
18 | nodemap::{NodeMapError, NodeTree}, | |
|
19 | revlog::{nodemap::NodeMap, RevlogIndex}, | |
|
20 | NodeError, Revision, | |
|
21 | }; | |
|
14 | 22 | use std::cell::RefCell; |
|
15 | 23 | |
|
16 | 24 | /// Return a Struct implementing the Graph trait |
@@ -26,6 +34,7 b' pub(crate) fn pyindex_to_graph(' | |||
|
26 | 34 | |
|
27 | 35 | py_class!(pub class MixedIndex |py| { |
|
28 | 36 | data cindex: RefCell<cindex::Index>; |
|
37 | data nt: RefCell<Option<NodeTree>>; | |
|
29 | 38 | |
|
30 | 39 | def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> { |
|
31 | 40 | Self::new(py, cindex) |
@@ -42,8 +51,99 b' py_class!(pub class MixedIndex |py| {' | |||
|
42 | 51 | Ok(self.cindex(py).borrow().inner().clone_ref(py)) |
|
43 | 52 | } |
|
44 | 53 | |
|
54 | // Index API involving nodemap, as defined in mercurial/pure/parsers.py | |
|
45 | 55 | |
|
56 | /// Return Revision if found, raises a bare `error.RevlogError` | |
|
57 | /// in case of ambiguity, same as C version does | |
|
58 | def get_rev(&self, node: PyBytes) -> PyResult<Option<Revision>> { | |
|
59 | let opt = self.get_nodetree(py)?.borrow(); | |
|
60 | let nt = opt.as_ref().unwrap(); | |
|
61 | let idx = &*self.cindex(py).borrow(); | |
|
62 | let node = node_from_py_bytes(py, &node)?; | |
|
63 | nt.find_bin(idx, (&node).into()).map_err(|e| nodemap_error(py, e)) | |
|
64 | } | |
|
65 | ||
|
66 | /// same as `get_rev()` but raises a bare `error.RevlogError` if node | |
|
67 | /// is not found. | |
|
68 | /// | |
|
69 | /// No need to repeat `node` in the exception, `mercurial/revlog.py` | |
|
70 | /// will catch and rewrap with it | |
|
71 | def rev(&self, node: PyBytes) -> PyResult<Revision> { | |
|
72 | self.get_rev(py, node)?.ok_or_else(|| revlog_error(py)) | |
|
73 | } | |
|
74 | ||
|
75 | /// return True if the node exist in the index | |
|
76 | def has_node(&self, node: PyBytes) -> PyResult<bool> { | |
|
77 | self.get_rev(py, node).map(|opt| opt.is_some()) | |
|
78 | } | |
|
79 | ||
|
80 | /// find length of shortest hex nodeid of a binary ID | |
|
81 | def shortest(&self, node: PyBytes) -> PyResult<usize> { | |
|
82 | let opt = self.get_nodetree(py)?.borrow(); | |
|
83 | let nt = opt.as_ref().unwrap(); | |
|
84 | let idx = &*self.cindex(py).borrow(); | |
|
85 | match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?) | |
|
86 | { | |
|
87 | Ok(Some(l)) => Ok(l), | |
|
88 | Ok(None) => Err(revlog_error(py)), | |
|
89 | Err(e) => Err(nodemap_error(py, e)), | |
|
90 | } | |
|
91 | } | |
|
92 | ||
|
93 | def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> { | |
|
94 | let opt = self.get_nodetree(py)?.borrow(); | |
|
95 | let nt = opt.as_ref().unwrap(); | |
|
96 | let idx = &*self.cindex(py).borrow(); | |
|
97 | ||
|
98 | let node_as_string = if cfg!(feature = "python3-sys") { | |
|
99 | node.cast_as::<PyString>(py)?.to_string(py)?.to_string() | |
|
100 | } | |
|
101 | else { | |
|
102 | let node = node.extract::<PyBytes>(py)?; | |
|
103 | String::from_utf8_lossy(node.data(py)).to_string() | |
|
104 | }; | |
|
105 | ||
|
106 | nt.find_hex(idx, &node_as_string) | |
|
107 | // TODO make an inner API returning the node directly | |
|
108 | .map(|opt| opt.map( | |
|
109 | |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes()))) | |
|
110 | .map_err(|e| nodemap_error(py, e)) | |
|
111 | ||
|
112 | } | |
|
113 | ||
|
114 | /// append an index entry | |
|
115 | def append(&self, tup: PyTuple) -> PyResult<PyObject> { | |
|
116 | if tup.len(py) < 8 { | |
|
117 | // this is better than the panic promised by tup.get_item() | |
|
118 | return Err( | |
|
119 | PyErr::new::<IndexError, _>(py, "tuple index out of range")) | |
|
120 | } | |
|
121 | let node_bytes = tup.get_item(py, 7).extract(py)?; | |
|
122 | let node = node_from_py_object(py, &node_bytes)?; | |
|
123 | ||
|
124 | let mut idx = self.cindex(py).borrow_mut(); | |
|
125 | let rev = idx.len() as Revision; | |
|
126 | ||
|
127 | idx.append(py, tup)?; | |
|
128 | self.get_nodetree(py)?.borrow_mut().as_mut().unwrap() | |
|
129 | .insert(&*idx, &node, rev) | |
|
130 | .map_err(|e| nodemap_error(py, e))?; | |
|
131 | Ok(py.None()) | |
|
132 | } | |
|
133 | ||
|
134 | def __delitem__(&self, key: PyObject) -> PyResult<()> { | |
|
135 | // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]` | |
|
136 | self.cindex(py).borrow().inner().del_item(py, key)?; | |
|
137 | let mut opt = self.get_nodetree(py)?.borrow_mut(); | |
|
138 | let mut nt = opt.as_mut().unwrap(); | |
|
139 | nt.invalidate_all(); | |
|
140 | self.fill_nodemap(py, &mut nt)?; | |
|
141 | Ok(()) | |
|
142 | } | |
|
143 | ||
|
144 | // | |
|
46 | 145 | // Reforwarded C index API |
|
146 | // | |
|
47 | 147 | |
|
48 | 148 | // index_methods (tp_methods). Same ordering as in revlog.c |
|
49 | 149 | |
@@ -67,21 +167,6 b' py_class!(pub class MixedIndex |py| {' | |||
|
67 | 167 | self.call_cindex(py, "get", args, kw) |
|
68 | 168 | } |
|
69 | 169 | |
|
70 | /// return `rev` associated with a node or None | |
|
71 | def get_rev(&self, *args, **kw) -> PyResult<PyObject> { | |
|
72 | self.call_cindex(py, "get_rev", args, kw) | |
|
73 | } | |
|
74 | ||
|
75 | /// return True if the node exist in the index | |
|
76 | def has_node(&self, *args, **kw) -> PyResult<PyObject> { | |
|
77 | self.call_cindex(py, "has_node", args, kw) | |
|
78 | } | |
|
79 | ||
|
80 | /// return `rev` associated with a node or raise RevlogError | |
|
81 | def rev(&self, *args, **kw) -> PyResult<PyObject> { | |
|
82 | self.call_cindex(py, "rev", args, kw) | |
|
83 | } | |
|
84 | ||
|
85 | 170 | /// compute phases |
|
86 | 171 | def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> { |
|
87 | 172 | self.call_cindex(py, "computephasesmapsets", args, kw) |
@@ -122,21 +207,6 b' py_class!(pub class MixedIndex |py| {' | |||
|
122 | 207 | self.call_cindex(py, "slicechunktodensity", args, kw) |
|
123 | 208 | } |
|
124 | 209 | |
|
125 | /// append an index entry | |
|
126 | def append(&self, *args, **kw) -> PyResult<PyObject> { | |
|
127 | self.call_cindex(py, "append", args, kw) | |
|
128 | } | |
|
129 | ||
|
130 | /// match a potentially ambiguous node ID | |
|
131 | def partialmatch(&self, *args, **kw) -> PyResult<PyObject> { | |
|
132 | self.call_cindex(py, "partialmatch", args, kw) | |
|
133 | } | |
|
134 | ||
|
135 | /// find length of shortest hex nodeid of a binary ID | |
|
136 | def shortest(&self, *args, **kw) -> PyResult<PyObject> { | |
|
137 | self.call_cindex(py, "shortest", args, kw) | |
|
138 | } | |
|
139 | ||
|
140 | 210 | /// stats for the index |
|
141 | 211 | def stats(&self, *args, **kw) -> PyResult<PyObject> { |
|
142 | 212 | self.call_cindex(py, "stats", args, kw) |
@@ -170,10 +240,6 b' py_class!(pub class MixedIndex |py| {' | |||
|
170 | 240 | self.cindex(py).borrow().inner().set_item(py, key, value) |
|
171 | 241 | } |
|
172 | 242 | |
|
173 | def __delitem__(&self, key: PyObject) -> PyResult<()> { | |
|
174 | self.cindex(py).borrow().inner().del_item(py, key) | |
|
175 | } | |
|
176 | ||
|
177 | 243 | def __contains__(&self, item: PyObject) -> PyResult<bool> { |
|
178 | 244 | // ObjectProtocol does not seem to provide contains(), so |
|
179 | 245 | // this is an equivalent implementation of the index_contains() |
@@ -202,9 +268,41 b' impl MixedIndex {' | |||
|
202 | 268 | Self::create_instance( |
|
203 | 269 | py, |
|
204 | 270 | RefCell::new(cindex::Index::new(py, cindex)?), |
|
271 | RefCell::new(None), | |
|
205 | 272 | ) |
|
206 | 273 | } |
|
207 | 274 | |
|
275 | /// This is scaffolding at this point, but it could also become | |
|
276 | /// a way to start a persistent nodemap or perform a | |
|
277 | /// vacuum / repack operation | |
|
278 | fn fill_nodemap( | |
|
279 | &self, | |
|
280 | py: Python, | |
|
281 | nt: &mut NodeTree, | |
|
282 | ) -> PyResult<PyObject> { | |
|
283 | let index = self.cindex(py).borrow(); | |
|
284 | for r in 0..index.len() { | |
|
285 | let rev = r as Revision; | |
|
286 | // in this case node() won't ever return None | |
|
287 | nt.insert(&*index, index.node(rev).unwrap(), rev) | |
|
288 | .map_err(|e| nodemap_error(py, e))? | |
|
289 | } | |
|
290 | Ok(py.None()) | |
|
291 | } | |
|
292 | ||
|
293 | fn get_nodetree<'a>( | |
|
294 | &'a self, | |
|
295 | py: Python<'a>, | |
|
296 | ) -> PyResult<&'a RefCell<Option<NodeTree>>> { | |
|
297 | if self.nt(py).borrow().is_none() { | |
|
298 | let readonly = Box::new(Vec::new()); | |
|
299 | let mut nt = NodeTree::load_bytes(readonly, 0); | |
|
300 | self.fill_nodemap(py, &mut nt)?; | |
|
301 | self.nt(py).borrow_mut().replace(nt); | |
|
302 | } | |
|
303 | Ok(self.nt(py)) | |
|
304 | } | |
|
305 | ||
|
208 | 306 | /// forward a method call to the underlying C index |
|
209 | 307 | fn call_cindex( |
|
210 | 308 | &self, |
General Comments 0
You need to be logged in to leave comments.
Login now