##// END OF EJS Templates
rust-index: renamed nodemap error function for rev not in index...
Georges Racinet -
r52092:44fbb7df default
parent child Browse files
Show More
@@ -1,639 +1,639 b''
1 1 // revlog.rs
2 2 //
3 3 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
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 use crate::{
9 9 cindex,
10 10 utils::{node_from_py_bytes, node_from_py_object},
11 11 PyRevision,
12 12 };
13 13 use cpython::{
14 14 buffer::{Element, PyBuffer},
15 15 exc::{IndexError, ValueError},
16 16 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyInt, PyModule,
17 17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
18 18 };
19 19 use hg::{
20 20 index::IndexHeader,
21 21 index::{RevisionDataParams, COMPRESSION_MODE_INLINE},
22 22 nodemap::{Block, NodeMapError, NodeTree},
23 23 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
24 24 BaseRevision, Revision, UncheckedRevision,
25 25 };
26 26 use std::cell::RefCell;
27 27
28 28 /// Return a Struct implementing the Graph trait
29 29 pub(crate) fn pyindex_to_graph(
30 30 py: Python,
31 31 index: PyObject,
32 32 ) -> PyResult<cindex::Index> {
33 33 match index.extract::<MixedIndex>(py) {
34 34 Ok(midx) => Ok(midx.clone_cindex(py)),
35 35 Err(_) => cindex::Index::new(py, index),
36 36 }
37 37 }
38 38
39 39 py_class!(pub class MixedIndex |py| {
40 40 data cindex: RefCell<cindex::Index>;
41 41 data index: RefCell<hg::index::Index>;
42 42 data nt: RefCell<Option<NodeTree>>;
43 43 data docket: RefCell<Option<PyObject>>;
44 44 // Holds a reference to the mmap'ed persistent nodemap data
45 45 data nodemap_mmap: RefCell<Option<PyBuffer>>;
46 46 // Holds a reference to the mmap'ed persistent index data
47 47 data index_mmap: RefCell<Option<PyBuffer>>;
48 48
49 49 def __new__(
50 50 _cls,
51 51 cindex: PyObject,
52 52 data: PyObject,
53 53 default_header: u32,
54 54 ) -> PyResult<MixedIndex> {
55 55 Self::new(py, cindex, data, default_header)
56 56 }
57 57
58 58 /// Compatibility layer used for Python consumers needing access to the C index
59 59 ///
60 60 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
61 61 /// that may need to build a custom `nodetree`, based on a specified revset.
62 62 /// With a Rust implementation of the nodemap, we will be able to get rid of
63 63 /// this, by exposing our own standalone nodemap class,
64 64 /// ready to accept `MixedIndex`.
65 65 def get_cindex(&self) -> PyResult<PyObject> {
66 66 Ok(self.cindex(py).borrow().inner().clone_ref(py))
67 67 }
68 68
69 69 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
70 70
71 71 /// Return Revision if found, raises a bare `error.RevlogError`
72 72 /// in case of ambiguity, same as C version does
73 73 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
74 74 let opt = self.get_nodetree(py)?.borrow();
75 75 let nt = opt.as_ref().unwrap();
76 76 let idx = &*self.cindex(py).borrow();
77 77 let ridx = &*self.index(py).borrow();
78 78 let node = node_from_py_bytes(py, &node)?;
79 79 let rust_rev =
80 80 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
81 81 let c_rev =
82 82 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?;
83 83 assert_eq!(rust_rev, c_rev);
84 84 Ok(rust_rev.map(Into::into))
85 85
86 86 }
87 87
88 88 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
89 89 /// is not found.
90 90 ///
91 91 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
92 92 /// will catch and rewrap with it
93 93 def rev(&self, node: PyBytes) -> PyResult<PyRevision> {
94 94 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
95 95 }
96 96
97 97 /// return True if the node exist in the index
98 98 def has_node(&self, node: PyBytes) -> PyResult<bool> {
99 99 self.get_rev(py, node).map(|opt| opt.is_some())
100 100 }
101 101
102 102 /// find length of shortest hex nodeid of a binary ID
103 103 def shortest(&self, node: PyBytes) -> PyResult<usize> {
104 104 let opt = self.get_nodetree(py)?.borrow();
105 105 let nt = opt.as_ref().unwrap();
106 106 let idx = &*self.cindex(py).borrow();
107 107 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
108 108 {
109 109 Ok(Some(l)) => Ok(l),
110 110 Ok(None) => Err(revlog_error(py)),
111 111 Err(e) => Err(nodemap_error(py, e)),
112 112 }
113 113 }
114 114
115 115 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
116 116 let opt = self.get_nodetree(py)?.borrow();
117 117 let nt = opt.as_ref().unwrap();
118 118 let idx = &*self.cindex(py).borrow();
119 119
120 120 let node_as_string = if cfg!(feature = "python3-sys") {
121 121 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
122 122 }
123 123 else {
124 124 let node = node.extract::<PyBytes>(py)?;
125 125 String::from_utf8_lossy(node.data(py)).to_string()
126 126 };
127 127
128 128 let prefix = NodePrefix::from_hex(&node_as_string)
129 129 .map_err(|_| PyErr::new::<ValueError, _>(
130 130 py, format!("Invalid node or prefix '{}'", node_as_string))
131 131 )?;
132 132
133 133 nt.find_bin(idx, prefix)
134 134 // TODO make an inner API returning the node directly
135 135 .map(|opt| opt.map(
136 136 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
137 137 .map_err(|e| nodemap_error(py, e))
138 138
139 139 }
140 140
141 141 /// append an index entry
142 142 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
143 143 if tup.len(py) < 8 {
144 144 // this is better than the panic promised by tup.get_item()
145 145 return Err(
146 146 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
147 147 }
148 148 let node_bytes = tup.get_item(py, 7).extract(py)?;
149 149 let node = node_from_py_object(py, &node_bytes)?;
150 150
151 151 let rev = self.len(py)? as BaseRevision;
152 152 let mut idx = self.cindex(py).borrow_mut();
153 153
154 154 // This is ok since we will just add the revision to the index
155 155 let rev = Revision(rev);
156 156 idx.append(py, tup.clone_ref(py))?;
157 157 self.index(py)
158 158 .borrow_mut()
159 159 .append(py_tuple_to_revision_data_params(py, tup)?)
160 160 .unwrap();
161 161 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
162 162 .insert(&*idx, &node, rev)
163 163 .map_err(|e| nodemap_error(py, e))?;
164 164 Ok(py.None())
165 165 }
166 166
167 167 def __delitem__(&self, key: PyObject) -> PyResult<()> {
168 168 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
169 169 self.cindex(py).borrow().inner().del_item(py, &key)?;
170 170 let start = key.getattr(py, "start")?;
171 171 let start = UncheckedRevision(start.extract(py)?);
172 172 let start = self.index(py)
173 173 .borrow()
174 174 .check_revision(start)
175 175 .ok_or_else(|| {
176 176 nodemap_error(py, NodeMapError::RevisionNotInIndex(start))
177 177 })?;
178 178 self.index(py).borrow_mut().remove(start).unwrap();
179 179 let mut opt = self.get_nodetree(py)?.borrow_mut();
180 180 let nt = opt.as_mut().unwrap();
181 181 nt.invalidate_all();
182 182 self.fill_nodemap(py, nt)?;
183 183 Ok(())
184 184 }
185 185
186 186 //
187 187 // Reforwarded C index API
188 188 //
189 189
190 190 // index_methods (tp_methods). Same ordering as in revlog.c
191 191
192 192 /// return the gca set of the given revs
193 193 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
194 194 self.call_cindex(py, "ancestors", args, kw)
195 195 }
196 196
197 197 /// return the heads of the common ancestors of the given revs
198 198 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
199 199 self.call_cindex(py, "commonancestorsheads", args, kw)
200 200 }
201 201
202 202 /// Clear the index caches and inner py_class data.
203 203 /// It is Python's responsibility to call `update_nodemap_data` again.
204 204 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
205 205 self.nt(py).borrow_mut().take();
206 206 self.docket(py).borrow_mut().take();
207 207 self.nodemap_mmap(py).borrow_mut().take();
208 208 self.index(py).borrow_mut().clear_caches();
209 209 self.call_cindex(py, "clearcaches", args, kw)
210 210 }
211 211
212 212 /// return the raw binary string representing a revision
213 213 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> {
214 214 self.call_cindex(py, "entry_binary", args, kw)
215 215 }
216 216
217 217 /// return a binary packed version of the header
218 218 def pack_header(&self, *args, **kw) -> PyResult<PyObject> {
219 219 let rindex = self.index(py).borrow();
220 220 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
221 221 let packed = PyBytes::new(py, &packed);
222 222 let cpacked = self.call_cindex(py, "pack_header", args, kw)?;
223 223 assert!(packed.as_object().compare(py, cpacked)?.is_eq());
224 224 Ok(packed.into_object())
225 225 }
226 226
227 227 /// get an index entry
228 228 def get(&self, *args, **kw) -> PyResult<PyObject> {
229 229 self.call_cindex(py, "get", args, kw)
230 230 }
231 231
232 232 /// compute phases
233 233 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
234 234 self.call_cindex(py, "computephasesmapsets", args, kw)
235 235 }
236 236
237 237 /// reachableroots
238 238 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
239 239 self.call_cindex(py, "reachableroots2", args, kw)
240 240 }
241 241
242 242 /// get head revisions
243 243 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
244 244 self.call_cindex(py, "headrevs", args, kw)
245 245 }
246 246
247 247 /// get filtered head revisions
248 248 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
249 249 self.call_cindex(py, "headrevsfiltered", args, kw)
250 250 }
251 251
252 252 /// True if the object is a snapshot
253 253 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
254 254 self.call_cindex(py, "issnapshot", args, kw)
255 255 }
256 256
257 257 /// Gather snapshot data in a cache dict
258 258 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
259 259 self.call_cindex(py, "findsnapshots", args, kw)
260 260 }
261 261
262 262 /// determine revisions with deltas to reconstruct fulltext
263 263 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
264 264 self.call_cindex(py, "deltachain", args, kw)
265 265 }
266 266
267 267 /// slice planned chunk read to reach a density threshold
268 268 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
269 269 self.call_cindex(py, "slicechunktodensity", args, kw)
270 270 }
271 271
272 272 /// stats for the index
273 273 def stats(&self, *args, **kw) -> PyResult<PyObject> {
274 274 self.call_cindex(py, "stats", args, kw)
275 275 }
276 276
277 277 // index_sequence_methods and index_mapping_methods.
278 278 //
279 279 // Since we call back through the high level Python API,
280 280 // there's no point making a distinction between index_get
281 281 // and index_getitem.
282 282
283 283 def __len__(&self) -> PyResult<usize> {
284 284 self.len(py)
285 285 }
286 286
287 287 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
288 288 // this conversion seems needless, but that's actually because
289 289 // `index_getitem` does not handle conversion from PyLong,
290 290 // which expressions such as [e for e in index] internally use.
291 291 // Note that we don't seem to have a direct way to call
292 292 // PySequence_GetItem (does the job), which would possibly be better
293 293 // for performance
294 294 let key = match key.extract::<i32>(py) {
295 295 Ok(rev) => rev.to_py_object(py).into_object(),
296 296 Err(_) => key,
297 297 };
298 298 self.cindex(py).borrow().inner().get_item(py, key)
299 299 }
300 300
301 301 def __contains__(&self, item: PyObject) -> PyResult<bool> {
302 302 // ObjectProtocol does not seem to provide contains(), so
303 303 // this is an equivalent implementation of the index_contains()
304 304 // defined in revlog.c
305 305 let cindex = self.cindex(py).borrow();
306 306 match item.extract::<i32>(py) {
307 307 Ok(rev) => {
308 308 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
309 309 }
310 310 Err(_) => {
311 311 cindex.inner().call_method(
312 312 py,
313 313 "has_node",
314 314 PyTuple::new(py, &[item]),
315 315 None)?
316 316 .extract(py)
317 317 }
318 318 }
319 319 }
320 320
321 321 def nodemap_data_all(&self) -> PyResult<PyBytes> {
322 322 self.inner_nodemap_data_all(py)
323 323 }
324 324
325 325 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
326 326 self.inner_nodemap_data_incremental(py)
327 327 }
328 328 def update_nodemap_data(
329 329 &self,
330 330 docket: PyObject,
331 331 nm_data: PyObject
332 332 ) -> PyResult<PyObject> {
333 333 self.inner_update_nodemap_data(py, docket, nm_data)
334 334 }
335 335
336 336 @property
337 337 def entry_size(&self) -> PyResult<PyInt> {
338 338 self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py)
339 339 }
340 340
341 341 @property
342 342 def rust_ext_compat(&self) -> PyResult<PyInt> {
343 343 self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py)
344 344 }
345 345
346 346 });
347 347
348 348 /// Take a (potentially) mmap'ed buffer, and return the underlying Python
349 349 /// buffer along with the Rust slice into said buffer. We need to keep the
350 350 /// Python buffer around, otherwise we'd get a dangling pointer once the buffer
351 351 /// is freed from Python's side.
352 352 ///
353 353 /// # Safety
354 354 ///
355 355 /// The caller must make sure that the buffer is kept around for at least as
356 356 /// long as the slice.
357 357 #[deny(unsafe_op_in_unsafe_fn)]
358 358 unsafe fn mmap_keeparound(
359 359 py: Python,
360 360 data: PyObject,
361 361 ) -> PyResult<(
362 362 PyBuffer,
363 363 Box<dyn std::ops::Deref<Target = [u8]> + Send + 'static>,
364 364 )> {
365 365 let buf = PyBuffer::get(py, &data)?;
366 366 let len = buf.item_count();
367 367
368 368 // Build a slice from the mmap'ed buffer data
369 369 let cbuf = buf.buf_ptr();
370 370 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
371 371 && buf.is_c_contiguous()
372 372 && u8::is_compatible_format(buf.format())
373 373 {
374 374 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
375 375 } else {
376 376 return Err(PyErr::new::<ValueError, _>(
377 377 py,
378 378 "Nodemap data buffer has an invalid memory representation"
379 379 .to_string(),
380 380 ));
381 381 };
382 382
383 383 Ok((buf, Box::new(bytes)))
384 384 }
385 385
386 386 fn py_tuple_to_revision_data_params(
387 387 py: Python,
388 388 tuple: PyTuple,
389 389 ) -> PyResult<RevisionDataParams> {
390 390 if tuple.len(py) < 8 {
391 391 // this is better than the panic promised by tup.get_item()
392 392 return Err(PyErr::new::<IndexError, _>(
393 393 py,
394 394 "tuple index out of range",
395 395 ));
396 396 }
397 397 let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?;
398 398 let node_id = tuple
399 399 .get_item(py, 7)
400 400 .extract::<PyBytes>(py)?
401 401 .data(py)
402 402 .try_into()
403 403 .unwrap();
404 404 let flags = (offset_or_flags & 0xFFFF) as u16;
405 405 let data_offset = offset_or_flags >> 16;
406 406 Ok(RevisionDataParams {
407 407 flags,
408 408 data_offset,
409 409 data_compressed_length: tuple.get_item(py, 1).extract(py)?,
410 410 data_uncompressed_length: tuple.get_item(py, 2).extract(py)?,
411 411 data_delta_base: tuple.get_item(py, 3).extract(py)?,
412 412 link_rev: tuple.get_item(py, 4).extract(py)?,
413 413 parent_rev_1: tuple.get_item(py, 5).extract(py)?,
414 414 parent_rev_2: tuple.get_item(py, 6).extract(py)?,
415 415 node_id,
416 416 _sidedata_offset: 0,
417 417 _sidedata_compressed_length: 0,
418 418 data_compression_mode: COMPRESSION_MODE_INLINE,
419 419 _sidedata_compression_mode: COMPRESSION_MODE_INLINE,
420 420 _rank: -1,
421 421 })
422 422 }
423 423
424 424 impl MixedIndex {
425 425 fn new(
426 426 py: Python,
427 427 cindex: PyObject,
428 428 data: PyObject,
429 429 header: u32,
430 430 ) -> PyResult<MixedIndex> {
431 431 // Safety: we keep the buffer around inside the class as `index_mmap`
432 432 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
433 433
434 434 Self::create_instance(
435 435 py,
436 436 RefCell::new(cindex::Index::new(py, cindex)?),
437 437 RefCell::new(
438 438 hg::index::Index::new(
439 439 bytes,
440 440 IndexHeader::parse(&header.to_be_bytes())
441 441 .expect("default header is broken")
442 442 .unwrap(),
443 443 )
444 444 .unwrap(),
445 445 ),
446 446 RefCell::new(None),
447 447 RefCell::new(None),
448 448 RefCell::new(None),
449 449 RefCell::new(Some(buf)),
450 450 )
451 451 }
452 452
453 453 fn len(&self, py: Python) -> PyResult<usize> {
454 454 let rust_index_len = self.index(py).borrow().len();
455 455 let cindex_len = self.cindex(py).borrow().inner().len(py)?;
456 456 assert_eq!(rust_index_len, cindex_len);
457 457 Ok(cindex_len)
458 458 }
459 459
460 460 /// This is scaffolding at this point, but it could also become
461 461 /// a way to start a persistent nodemap or perform a
462 462 /// vacuum / repack operation
463 463 fn fill_nodemap(
464 464 &self,
465 465 py: Python,
466 466 nt: &mut NodeTree,
467 467 ) -> PyResult<PyObject> {
468 468 let index = self.cindex(py).borrow();
469 469 for r in 0..self.len(py)? {
470 470 let rev = Revision(r as BaseRevision);
471 471 // in this case node() won't ever return None
472 472 nt.insert(&*index, index.node(rev).unwrap(), rev)
473 473 .map_err(|e| nodemap_error(py, e))?
474 474 }
475 475 Ok(py.None())
476 476 }
477 477
478 478 fn get_nodetree<'a>(
479 479 &'a self,
480 480 py: Python<'a>,
481 481 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
482 482 if self.nt(py).borrow().is_none() {
483 483 let readonly = Box::<Vec<_>>::default();
484 484 let mut nt = NodeTree::load_bytes(readonly, 0);
485 485 self.fill_nodemap(py, &mut nt)?;
486 486 self.nt(py).borrow_mut().replace(nt);
487 487 }
488 488 Ok(self.nt(py))
489 489 }
490 490
491 491 /// forward a method call to the underlying C index
492 492 fn call_cindex(
493 493 &self,
494 494 py: Python,
495 495 name: &str,
496 496 args: &PyTuple,
497 497 kwargs: Option<&PyDict>,
498 498 ) -> PyResult<PyObject> {
499 499 self.cindex(py)
500 500 .borrow()
501 501 .inner()
502 502 .call_method(py, name, args, kwargs)
503 503 }
504 504
505 505 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
506 506 self.cindex(py).borrow().clone_ref(py)
507 507 }
508 508
509 509 /// Returns the full nodemap bytes to be written as-is to disk
510 510 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
511 511 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
512 512 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
513 513
514 514 // If there's anything readonly, we need to build the data again from
515 515 // scratch
516 516 let bytes = if readonly.len() > 0 {
517 517 let mut nt = NodeTree::load_bytes(Box::<Vec<_>>::default(), 0);
518 518 self.fill_nodemap(py, &mut nt)?;
519 519
520 520 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
521 521 assert_eq!(readonly.len(), 0);
522 522
523 523 bytes
524 524 } else {
525 525 bytes
526 526 };
527 527
528 528 let bytes = PyBytes::new(py, &bytes);
529 529 Ok(bytes)
530 530 }
531 531
532 532 /// Returns the last saved docket along with the size of any changed data
533 533 /// (in number of blocks), and said data as bytes.
534 534 fn inner_nodemap_data_incremental(
535 535 &self,
536 536 py: Python,
537 537 ) -> PyResult<PyObject> {
538 538 let docket = self.docket(py).borrow();
539 539 let docket = match docket.as_ref() {
540 540 Some(d) => d,
541 541 None => return Ok(py.None()),
542 542 };
543 543
544 544 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
545 545 let masked_blocks = node_tree.masked_readonly_blocks();
546 546 let (_, data) = node_tree.into_readonly_and_added_bytes();
547 547 let changed = masked_blocks * std::mem::size_of::<Block>();
548 548
549 549 Ok((docket, changed, PyBytes::new(py, &data))
550 550 .to_py_object(py)
551 551 .into_object())
552 552 }
553 553
554 554 /// Update the nodemap from the new (mmaped) data.
555 555 /// The docket is kept as a reference for later incremental calls.
556 556 fn inner_update_nodemap_data(
557 557 &self,
558 558 py: Python,
559 559 docket: PyObject,
560 560 nm_data: PyObject,
561 561 ) -> PyResult<PyObject> {
562 562 // Safety: we keep the buffer around inside the class as `nodemap_mmap`
563 563 let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? };
564 564 let len = buf.item_count();
565 565 self.nodemap_mmap(py).borrow_mut().replace(buf);
566 566
567 567 let mut nt = NodeTree::load_bytes(bytes, len);
568 568
569 569 let data_tip = docket
570 570 .getattr(py, "tip_rev")?
571 571 .extract::<BaseRevision>(py)?
572 572 .into();
573 573 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
574 574 let idx = self.cindex(py).borrow();
575 575 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
576 576 nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip))
577 577 })?;
578 578 let current_tip = idx.len();
579 579
580 580 for r in (data_tip.0 + 1)..current_tip as BaseRevision {
581 581 let rev = Revision(r);
582 582 // in this case node() won't ever return None
583 583 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
584 584 .map_err(|e| nodemap_error(py, e))?
585 585 }
586 586
587 587 *self.nt(py).borrow_mut() = Some(nt);
588 588
589 589 Ok(py.None())
590 590 }
591 591 }
592 592
593 593 fn revlog_error(py: Python) -> PyErr {
594 594 match py
595 595 .import("mercurial.error")
596 596 .and_then(|m| m.get(py, "RevlogError"))
597 597 {
598 598 Err(e) => e,
599 599 Ok(cls) => PyErr::from_instance(
600 600 py,
601 601 cls.call(py, (py.None(),), None).ok().into_py_object(py),
602 602 ),
603 603 }
604 604 }
605 605
606 fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
606 fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
607 607 PyErr::new::<ValueError, _>(
608 608 py,
609 609 format!(
610 610 "Inconsistency: Revision {} found in nodemap \
611 611 is not in revlog index",
612 612 rev
613 613 ),
614 614 )
615 615 }
616 616
617 617 /// Standard treatment of NodeMapError
618 618 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
619 619 match err {
620 620 NodeMapError::MultipleResults => revlog_error(py),
621 NodeMapError::RevisionNotInIndex(r) => rev_not_in_index(py, r),
621 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
622 622 }
623 623 }
624 624
625 625 /// Create the module, with __package__ given from parent
626 626 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
627 627 let dotted_name = &format!("{}.revlog", package);
628 628 let m = PyModule::new(py, dotted_name)?;
629 629 m.add(py, "__package__", package)?;
630 630 m.add(py, "__doc__", "RevLog - Rust implementations")?;
631 631
632 632 m.add_class::<MixedIndex>(py)?;
633 633
634 634 let sys = PyModule::import(py, "sys")?;
635 635 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
636 636 sys_modules.set_item(py, dotted_name, &m)?;
637 637
638 638 Ok(m)
639 639 }
General Comments 0
You need to be logged in to leave comments. Login now