##// END OF EJS Templates
rust: make `Revision` a newtype...
Raphaël Gomès -
r51872:4c5f6e95 default
parent child Browse files
Show More
@@ -29,7 +29,7 b' pub const INDEX_ENTRY_SIZE: usize = 64;'
29
29
30 impl IndexEntry {
30 impl IndexEntry {
31 fn parents(&self) -> [Revision; 2] {
31 fn parents(&self) -> [Revision; 2] {
32 [Revision::from_be(self.p1), Revision::from_be(self.p1)]
32 [self.p1, self.p2]
33 }
33 }
34 }
34 }
35
35
@@ -42,23 +42,18 b' impl RevlogIndex for Index {'
42 if rev == NULL_REVISION {
42 if rev == NULL_REVISION {
43 return None;
43 return None;
44 }
44 }
45 let i = rev as usize;
45 Some(&self.data[rev.0 as usize].node)
46 if i >= self.len() {
47 None
48 } else {
49 Some(&self.data[i].node)
50 }
51 }
46 }
52 }
47 }
53
48
54 impl Graph for &Index {
49 impl Graph for &Index {
55 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
50 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
56 let [p1, p2] = (*self).data[rev as usize].parents();
51 let [p1, p2] = self.data[rev.0 as usize].parents();
57 let len = (*self).len();
52 let len = (*self).len();
58 if p1 < NULL_REVISION
53 if p1 < NULL_REVISION
59 || p2 < NULL_REVISION
54 || p2 < NULL_REVISION
60 || p1 as usize >= len
55 || p1.0 as usize >= len
61 || p2 as usize >= len
56 || p2.0 as usize >= len
62 {
57 {
63 return Err(GraphError::ParentOutOfRange(rev));
58 return Err(GraphError::ParentOutOfRange(rev));
64 }
59 }
@@ -36,7 +36,7 b' fn create(index: &Index, path: &Path) ->'
36 let start = Instant::now();
36 let start = Instant::now();
37 let mut nm = NodeTree::default();
37 let mut nm = NodeTree::default();
38 for rev in 0..index.len() {
38 for rev in 0..index.len() {
39 let rev = rev as Revision;
39 let rev = Revision(rev as BaseRevision);
40 nm.insert(index, index.node(rev).unwrap(), rev).unwrap();
40 nm.insert(index, index.node(rev).unwrap(), rev).unwrap();
41 }
41 }
42 eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed());
42 eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed());
@@ -55,7 +55,11 b' fn bench(index: &Index, nm: &NodeTree, q'
55 let len = index.len() as u32;
55 let len = index.len() as u32;
56 let mut rng = rand::thread_rng();
56 let mut rng = rand::thread_rng();
57 let nodes: Vec<Node> = (0..queries)
57 let nodes: Vec<Node> = (0..queries)
58 .map(|_| *index.node((rng.gen::<u32>() % len) as Revision).unwrap())
58 .map(|_| {
59 *index
60 .node(Revision((rng.gen::<u32>() % len) as BaseRevision))
61 .unwrap()
62 })
59 .collect();
63 .collect();
60 if queries < 10 {
64 if queries < 10 {
61 let nodes_hex: Vec<String> =
65 let nodes_hex: Vec<String> =
@@ -247,7 +247,9 b' impl<G: Graph> MissingAncestors<G> {'
247 revs.remove(&curr);
247 revs.remove(&curr);
248 self.add_parents(curr)?;
248 self.add_parents(curr)?;
249 }
249 }
250 curr -= 1;
250 // We know this revision is safe because we've checked the bounds
251 // before.
252 curr = Revision(curr.0 - 1);
251 }
253 }
252 Ok(())
254 Ok(())
253 }
255 }
@@ -297,14 +299,14 b' impl<G: Graph> MissingAncestors<G> {'
297
299
298 // TODO heuristics for with_capacity()?
300 // TODO heuristics for with_capacity()?
299 let mut missing: Vec<Revision> = Vec::new();
301 let mut missing: Vec<Revision> = Vec::new();
300 for curr in (0..=start).rev() {
302 for curr in (0..=start.0).rev() {
301 if revs_visit.is_empty() {
303 if revs_visit.is_empty() {
302 break;
304 break;
303 }
305 }
304 if both_visit.remove(&curr) {
306 if both_visit.remove(&Revision(curr)) {
305 // curr's parents might have made it into revs_visit through
307 // curr's parents might have made it into revs_visit through
306 // another path
308 // another path
307 for p in self.graph.parents(curr)?.iter().cloned() {
309 for p in self.graph.parents(Revision(curr))?.iter().cloned() {
308 if p == NULL_REVISION {
310 if p == NULL_REVISION {
309 continue;
311 continue;
310 }
312 }
@@ -312,9 +314,9 b' impl<G: Graph> MissingAncestors<G> {'
312 bases_visit.insert(p);
314 bases_visit.insert(p);
313 both_visit.insert(p);
315 both_visit.insert(p);
314 }
316 }
315 } else if revs_visit.remove(&curr) {
317 } else if revs_visit.remove(&Revision(curr)) {
316 missing.push(curr);
318 missing.push(Revision(curr));
317 for p in self.graph.parents(curr)?.iter().cloned() {
319 for p in self.graph.parents(Revision(curr))?.iter().cloned() {
318 if p == NULL_REVISION {
320 if p == NULL_REVISION {
319 continue;
321 continue;
320 }
322 }
@@ -331,8 +333,8 b' impl<G: Graph> MissingAncestors<G> {'
331 revs_visit.insert(p);
333 revs_visit.insert(p);
332 }
334 }
333 }
335 }
334 } else if bases_visit.contains(&curr) {
336 } else if bases_visit.contains(&Revision(curr)) {
335 for p in self.graph.parents(curr)?.iter().cloned() {
337 for p in self.graph.parents(Revision(curr))?.iter().cloned() {
336 if p == NULL_REVISION {
338 if p == NULL_REVISION {
337 continue;
339 continue;
338 }
340 }
@@ -356,7 +358,41 b' impl<G: Graph> MissingAncestors<G> {'
356 mod tests {
358 mod tests {
357
359
358 use super::*;
360 use super::*;
359 use crate::testing::{SampleGraph, VecGraph};
361 use crate::{
362 testing::{SampleGraph, VecGraph},
363 BaseRevision,
364 };
365
366 impl From<BaseRevision> for Revision {
367 fn from(value: BaseRevision) -> Self {
368 if !cfg!(test) {
369 panic!("should only be used in tests")
370 }
371 Revision(value)
372 }
373 }
374
375 impl PartialEq<BaseRevision> for Revision {
376 fn eq(&self, other: &BaseRevision) -> bool {
377 if !cfg!(test) {
378 panic!("should only be used in tests")
379 }
380 self.0.eq(other)
381 }
382 }
383
384 impl PartialEq<u32> for Revision {
385 fn eq(&self, other: &u32) -> bool {
386 if !cfg!(test) {
387 panic!("should only be used in tests")
388 }
389 let check: Result<u32, _> = self.0.try_into();
390 match check {
391 Ok(value) => value.eq(other),
392 Err(_) => false,
393 }
394 }
395 }
360
396
361 fn list_ancestors<G: Graph>(
397 fn list_ancestors<G: Graph>(
362 graph: G,
398 graph: G,
@@ -374,37 +410,80 b' mod tests {'
374 /// Same tests as test-ancestor.py, without membership
410 /// Same tests as test-ancestor.py, without membership
375 /// (see also test-ancestor.py.out)
411 /// (see also test-ancestor.py.out)
376 fn test_list_ancestor() {
412 fn test_list_ancestor() {
377 assert_eq!(list_ancestors(SampleGraph, vec![], 0, false), vec![]);
413 assert_eq!(
414 list_ancestors(SampleGraph, vec![], 0.into(), false),
415 Vec::<Revision>::new()
416 );
378 assert_eq!(
417 assert_eq!(
379 list_ancestors(SampleGraph, vec![11, 13], 0, false),
418 list_ancestors(
419 SampleGraph,
420 vec![11.into(), 13.into()],
421 0.into(),
422 false
423 ),
380 vec![8, 7, 4, 3, 2, 1, 0]
424 vec![8, 7, 4, 3, 2, 1, 0]
381 );
425 );
382 assert_eq!(
426 assert_eq!(
383 list_ancestors(SampleGraph, vec![1, 3], 0, false),
427 list_ancestors(
428 SampleGraph,
429 vec![1.into(), 3.into()],
430 0.into(),
431 false
432 ),
384 vec![1, 0]
433 vec![1, 0]
385 );
434 );
386 assert_eq!(
435 assert_eq!(
387 list_ancestors(SampleGraph, vec![11, 13], 0, true),
436 list_ancestors(
437 SampleGraph,
438 vec![11.into(), 13.into()],
439 0.into(),
440 true
441 ),
388 vec![13, 11, 8, 7, 4, 3, 2, 1, 0]
442 vec![13, 11, 8, 7, 4, 3, 2, 1, 0]
389 );
443 );
390 assert_eq!(
444 assert_eq!(
391 list_ancestors(SampleGraph, vec![11, 13], 6, false),
445 list_ancestors(
446 SampleGraph,
447 vec![11.into(), 13.into()],
448 6.into(),
449 false
450 ),
392 vec![8, 7]
451 vec![8, 7]
393 );
452 );
394 assert_eq!(
453 assert_eq!(
395 list_ancestors(SampleGraph, vec![11, 13], 6, true),
454 list_ancestors(
455 SampleGraph,
456 vec![11.into(), 13.into()],
457 6.into(),
458 true
459 ),
396 vec![13, 11, 8, 7]
460 vec![13, 11, 8, 7]
397 );
461 );
398 assert_eq!(
462 assert_eq!(
399 list_ancestors(SampleGraph, vec![11, 13], 11, true),
463 list_ancestors(
464 SampleGraph,
465 vec![11.into(), 13.into()],
466 11.into(),
467 true
468 ),
400 vec![13, 11]
469 vec![13, 11]
401 );
470 );
402 assert_eq!(
471 assert_eq!(
403 list_ancestors(SampleGraph, vec![11, 13], 12, true),
472 list_ancestors(
473 SampleGraph,
474 vec![11.into(), 13.into()],
475 12.into(),
476 true
477 ),
404 vec![13]
478 vec![13]
405 );
479 );
406 assert_eq!(
480 assert_eq!(
407 list_ancestors(SampleGraph, vec![10, 1], 0, true),
481 list_ancestors(
482 SampleGraph,
483 vec![10.into(), 1.into()],
484 0.into(),
485 true
486 ),
408 vec![10, 5, 4, 2, 1, 0]
487 vec![10, 5, 4, 2, 1, 0]
409 );
488 );
410 }
489 }
@@ -415,33 +494,53 b' mod tests {'
415 /// suite.
494 /// suite.
416 /// For instance, run tests/test-obsolete-checkheads.t
495 /// For instance, run tests/test-obsolete-checkheads.t
417 fn test_nullrev_input() {
496 fn test_nullrev_input() {
418 let mut iter =
497 let mut iter = AncestorsIterator::new(
419 AncestorsIterator::new(SampleGraph, vec![-1], 0, false).unwrap();
498 SampleGraph,
499 vec![Revision(-1)],
500 0.into(),
501 false,
502 )
503 .unwrap();
420 assert_eq!(iter.next(), None)
504 assert_eq!(iter.next(), None)
421 }
505 }
422
506
423 #[test]
507 #[test]
424 fn test_contains() {
508 fn test_contains() {
425 let mut lazy =
509 let mut lazy = AncestorsIterator::new(
426 AncestorsIterator::new(SampleGraph, vec![10, 1], 0, true).unwrap();
510 SampleGraph,
427 assert!(lazy.contains(1).unwrap());
511 vec![10.into(), 1.into()],
428 assert!(!lazy.contains(3).unwrap());
512 0.into(),
513 true,
514 )
515 .unwrap();
516 assert!(lazy.contains(1.into()).unwrap());
517 assert!(!lazy.contains(3.into()).unwrap());
429
518
430 let mut lazy =
519 let mut lazy = AncestorsIterator::new(
431 AncestorsIterator::new(SampleGraph, vec![0], 0, false).unwrap();
520 SampleGraph,
521 vec![0.into()],
522 0.into(),
523 false,
524 )
525 .unwrap();
432 assert!(!lazy.contains(NULL_REVISION).unwrap());
526 assert!(!lazy.contains(NULL_REVISION).unwrap());
433 }
527 }
434
528
435 #[test]
529 #[test]
436 fn test_peek() {
530 fn test_peek() {
437 let mut iter =
531 let mut iter = AncestorsIterator::new(
438 AncestorsIterator::new(SampleGraph, vec![10], 0, true).unwrap();
532 SampleGraph,
533 vec![10.into()],
534 0.into(),
535 true,
536 )
537 .unwrap();
439 // peek() gives us the next value
538 // peek() gives us the next value
440 assert_eq!(iter.peek(), Some(10));
539 assert_eq!(iter.peek(), Some(10.into()));
441 // but it's not been consumed
540 // but it's not been consumed
442 assert_eq!(iter.next(), Some(Ok(10)));
541 assert_eq!(iter.next(), Some(Ok(10.into())));
443 // and iteration resumes normally
542 // and iteration resumes normally
444 assert_eq!(iter.next(), Some(Ok(5)));
543 assert_eq!(iter.next(), Some(Ok(5.into())));
445
544
446 // let's drain the iterator to test peek() at the end
545 // let's drain the iterator to test peek() at the end
447 while iter.next().is_some() {}
546 while iter.next().is_some() {}
@@ -450,19 +549,29 b' mod tests {'
450
549
451 #[test]
550 #[test]
452 fn test_empty() {
551 fn test_empty() {
453 let mut iter =
552 let mut iter = AncestorsIterator::new(
454 AncestorsIterator::new(SampleGraph, vec![10], 0, true).unwrap();
553 SampleGraph,
554 vec![10.into()],
555 0.into(),
556 true,
557 )
558 .unwrap();
455 assert!(!iter.is_empty());
559 assert!(!iter.is_empty());
456 while iter.next().is_some() {}
560 while iter.next().is_some() {}
457 assert!(!iter.is_empty());
561 assert!(!iter.is_empty());
458
562
459 let iter =
563 let iter = AncestorsIterator::new(SampleGraph, vec![], 0.into(), true)
460 AncestorsIterator::new(SampleGraph, vec![], 0, true).unwrap();
564 .unwrap();
461 assert!(iter.is_empty());
565 assert!(iter.is_empty());
462
566
463 // case where iter.seen == {NULL_REVISION}
567 // case where iter.seen == {NULL_REVISION}
464 let iter =
568 let iter = AncestorsIterator::new(
465 AncestorsIterator::new(SampleGraph, vec![0], 0, false).unwrap();
569 SampleGraph,
570 vec![0.into()],
571 0.into(),
572 false,
573 )
574 .unwrap();
466 assert!(iter.is_empty());
575 assert!(iter.is_empty());
467 }
576 }
468
577
@@ -471,9 +580,11 b' mod tests {'
471 struct Corrupted;
580 struct Corrupted;
472
581
473 impl Graph for Corrupted {
582 impl Graph for Corrupted {
583 // FIXME what to do about this? Are we just not supposed to get them
584 // anymore?
474 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
585 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
475 match rev {
586 match rev {
476 1 => Ok([0, -1]),
587 Revision(1) => Ok([0.into(), (-1).into()]),
477 r => Err(GraphError::ParentOutOfRange(r)),
588 r => Err(GraphError::ParentOutOfRange(r)),
478 }
589 }
479 }
590 }
@@ -482,9 +593,14 b' mod tests {'
482 #[test]
593 #[test]
483 fn test_initrev_out_of_range() {
594 fn test_initrev_out_of_range() {
484 // inclusive=false looks up initrev's parents right away
595 // inclusive=false looks up initrev's parents right away
485 match AncestorsIterator::new(SampleGraph, vec![25], 0, false) {
596 match AncestorsIterator::new(
597 SampleGraph,
598 vec![25.into()],
599 0.into(),
600 false,
601 ) {
486 Ok(_) => panic!("Should have been ParentOutOfRange"),
602 Ok(_) => panic!("Should have been ParentOutOfRange"),
487 Err(e) => assert_eq!(e, GraphError::ParentOutOfRange(25)),
603 Err(e) => assert_eq!(e, GraphError::ParentOutOfRange(25.into())),
488 }
604 }
489 }
605 }
490
606
@@ -492,22 +608,29 b' mod tests {'
492 fn test_next_out_of_range() {
608 fn test_next_out_of_range() {
493 // inclusive=false looks up initrev's parents right away
609 // inclusive=false looks up initrev's parents right away
494 let mut iter =
610 let mut iter =
495 AncestorsIterator::new(Corrupted, vec![1], 0, false).unwrap();
611 AncestorsIterator::new(Corrupted, vec![1.into()], 0.into(), false)
496 assert_eq!(iter.next(), Some(Err(GraphError::ParentOutOfRange(0))));
612 .unwrap();
613 assert_eq!(
614 iter.next(),
615 Some(Err(GraphError::ParentOutOfRange(0.into())))
616 );
497 }
617 }
498
618
499 #[test]
619 #[test]
500 /// Test constructor, add/get bases and heads
620 /// Test constructor, add/get bases and heads
501 fn test_missing_bases() -> Result<(), GraphError> {
621 fn test_missing_bases() -> Result<(), GraphError> {
502 let mut missing_ancestors =
622 let mut missing_ancestors = MissingAncestors::new(
503 MissingAncestors::new(SampleGraph, [5, 3, 1, 3].iter().cloned());
623 SampleGraph,
624 [5.into(), 3.into(), 1.into(), 3.into()].iter().cloned(),
625 );
504 let mut as_vec: Vec<Revision> =
626 let mut as_vec: Vec<Revision> =
505 missing_ancestors.get_bases().iter().cloned().collect();
627 missing_ancestors.get_bases().iter().cloned().collect();
506 as_vec.sort_unstable();
628 as_vec.sort_unstable();
507 assert_eq!(as_vec, [1, 3, 5]);
629 assert_eq!(as_vec, [1, 3, 5]);
508 assert_eq!(missing_ancestors.max_base, 5);
630 assert_eq!(missing_ancestors.max_base, 5);
509
631
510 missing_ancestors.add_bases([3, 7, 8].iter().cloned());
632 missing_ancestors
633 .add_bases([3.into(), 7.into(), 8.into()].iter().cloned());
511 as_vec = missing_ancestors.get_bases().iter().cloned().collect();
634 as_vec = missing_ancestors.get_bases().iter().cloned().collect();
512 as_vec.sort_unstable();
635 as_vec.sort_unstable();
513 assert_eq!(as_vec, [1, 3, 5, 7, 8]);
636 assert_eq!(as_vec, [1, 3, 5, 7, 8]);
@@ -520,13 +643,16 b' mod tests {'
520 }
643 }
521
644
522 fn assert_missing_remove(
645 fn assert_missing_remove(
523 bases: &[Revision],
646 bases: &[BaseRevision],
524 revs: &[Revision],
647 revs: &[BaseRevision],
525 expected: &[Revision],
648 expected: &[BaseRevision],
526 ) {
649 ) {
527 let mut missing_ancestors =
650 let mut missing_ancestors = MissingAncestors::new(
528 MissingAncestors::new(SampleGraph, bases.iter().cloned());
651 SampleGraph,
529 let mut revset: HashSet<Revision> = revs.iter().cloned().collect();
652 bases.iter().map(|r| Revision(*r)),
653 );
654 let mut revset: HashSet<Revision> =
655 revs.iter().map(|r| Revision(*r)).collect();
530 missing_ancestors
656 missing_ancestors
531 .remove_ancestors_from(&mut revset)
657 .remove_ancestors_from(&mut revset)
532 .unwrap();
658 .unwrap();
@@ -547,14 +673,16 b' mod tests {'
547 }
673 }
548
674
549 fn assert_missing_ancestors(
675 fn assert_missing_ancestors(
550 bases: &[Revision],
676 bases: &[BaseRevision],
551 revs: &[Revision],
677 revs: &[BaseRevision],
552 expected: &[Revision],
678 expected: &[BaseRevision],
553 ) {
679 ) {
554 let mut missing_ancestors =
680 let mut missing_ancestors = MissingAncestors::new(
555 MissingAncestors::new(SampleGraph, bases.iter().cloned());
681 SampleGraph,
682 bases.iter().map(|r| Revision(*r)),
683 );
556 let missing = missing_ancestors
684 let missing = missing_ancestors
557 .missing_ancestors(revs.iter().cloned())
685 .missing_ancestors(revs.iter().map(|r| Revision(*r)))
558 .unwrap();
686 .unwrap();
559 assert_eq!(missing.as_slice(), expected);
687 assert_eq!(missing.as_slice(), expected);
560 }
688 }
@@ -575,110 +703,115 b' mod tests {'
575 #[allow(clippy::unnecessary_cast)]
703 #[allow(clippy::unnecessary_cast)]
576 #[test]
704 #[test]
577 fn test_remove_ancestors_from_case1() {
705 fn test_remove_ancestors_from_case1() {
706 const FAKE_NULL_REVISION: BaseRevision = -1;
707 assert_eq!(FAKE_NULL_REVISION, NULL_REVISION.0);
578 let graph: VecGraph = vec![
708 let graph: VecGraph = vec![
579 [NULL_REVISION, NULL_REVISION],
709 [FAKE_NULL_REVISION, FAKE_NULL_REVISION],
580 [0, NULL_REVISION],
710 [0, FAKE_NULL_REVISION],
581 [1, 0],
711 [1, 0],
582 [2, 1],
712 [2, 1],
583 [3, NULL_REVISION],
713 [3, FAKE_NULL_REVISION],
584 [4, NULL_REVISION],
714 [4, FAKE_NULL_REVISION],
585 [5, 1],
715 [5, 1],
586 [2, NULL_REVISION],
716 [2, FAKE_NULL_REVISION],
587 [7, NULL_REVISION],
717 [7, FAKE_NULL_REVISION],
588 [8, NULL_REVISION],
718 [8, FAKE_NULL_REVISION],
589 [9, NULL_REVISION],
719 [9, FAKE_NULL_REVISION],
590 [10, 1],
720 [10, 1],
591 [3, NULL_REVISION],
721 [3, FAKE_NULL_REVISION],
592 [12, NULL_REVISION],
722 [12, FAKE_NULL_REVISION],
593 [13, NULL_REVISION],
723 [13, FAKE_NULL_REVISION],
594 [14, NULL_REVISION],
724 [14, FAKE_NULL_REVISION],
595 [4, NULL_REVISION],
725 [4, FAKE_NULL_REVISION],
596 [16, NULL_REVISION],
726 [16, FAKE_NULL_REVISION],
597 [17, NULL_REVISION],
727 [17, FAKE_NULL_REVISION],
598 [18, NULL_REVISION],
728 [18, FAKE_NULL_REVISION],
599 [19, 11],
729 [19, 11],
600 [20, NULL_REVISION],
730 [20, FAKE_NULL_REVISION],
601 [21, NULL_REVISION],
731 [21, FAKE_NULL_REVISION],
602 [22, NULL_REVISION],
732 [22, FAKE_NULL_REVISION],
603 [23, NULL_REVISION],
733 [23, FAKE_NULL_REVISION],
604 [2, NULL_REVISION],
734 [2, FAKE_NULL_REVISION],
605 [3, NULL_REVISION],
735 [3, FAKE_NULL_REVISION],
606 [26, 24],
736 [26, 24],
607 [27, NULL_REVISION],
737 [27, FAKE_NULL_REVISION],
608 [28, NULL_REVISION],
738 [28, FAKE_NULL_REVISION],
609 [12, NULL_REVISION],
739 [12, FAKE_NULL_REVISION],
610 [1, NULL_REVISION],
740 [1, FAKE_NULL_REVISION],
611 [1, 9],
741 [1, 9],
612 [32, NULL_REVISION],
742 [32, FAKE_NULL_REVISION],
613 [33, NULL_REVISION],
743 [33, FAKE_NULL_REVISION],
614 [34, 31],
744 [34, 31],
615 [35, NULL_REVISION],
745 [35, FAKE_NULL_REVISION],
616 [36, 26],
746 [36, 26],
617 [37, NULL_REVISION],
747 [37, FAKE_NULL_REVISION],
618 [38, NULL_REVISION],
748 [38, FAKE_NULL_REVISION],
619 [39, NULL_REVISION],
749 [39, FAKE_NULL_REVISION],
620 [40, NULL_REVISION],
750 [40, FAKE_NULL_REVISION],
621 [41, NULL_REVISION],
751 [41, FAKE_NULL_REVISION],
622 [42, 26],
752 [42, 26],
623 [0, NULL_REVISION],
753 [0, FAKE_NULL_REVISION],
624 [44, NULL_REVISION],
754 [44, FAKE_NULL_REVISION],
625 [45, 4],
755 [45, 4],
626 [40, NULL_REVISION],
756 [40, FAKE_NULL_REVISION],
627 [47, NULL_REVISION],
757 [47, FAKE_NULL_REVISION],
628 [36, 0],
758 [36, 0],
629 [49, NULL_REVISION],
759 [49, FAKE_NULL_REVISION],
630 [NULL_REVISION, NULL_REVISION],
760 [FAKE_NULL_REVISION, FAKE_NULL_REVISION],
631 [51, NULL_REVISION],
761 [51, FAKE_NULL_REVISION],
632 [52, NULL_REVISION],
762 [52, FAKE_NULL_REVISION],
633 [53, NULL_REVISION],
763 [53, FAKE_NULL_REVISION],
634 [14, NULL_REVISION],
764 [14, FAKE_NULL_REVISION],
635 [55, NULL_REVISION],
765 [55, FAKE_NULL_REVISION],
636 [15, NULL_REVISION],
766 [15, FAKE_NULL_REVISION],
637 [23, NULL_REVISION],
767 [23, FAKE_NULL_REVISION],
638 [58, NULL_REVISION],
768 [58, FAKE_NULL_REVISION],
639 [59, NULL_REVISION],
769 [59, FAKE_NULL_REVISION],
640 [2, NULL_REVISION],
770 [2, FAKE_NULL_REVISION],
641 [61, 59],
771 [61, 59],
642 [62, NULL_REVISION],
772 [62, FAKE_NULL_REVISION],
643 [63, NULL_REVISION],
773 [63, FAKE_NULL_REVISION],
644 [NULL_REVISION, NULL_REVISION],
774 [FAKE_NULL_REVISION, FAKE_NULL_REVISION],
645 [65, NULL_REVISION],
775 [65, FAKE_NULL_REVISION],
646 [66, NULL_REVISION],
776 [66, FAKE_NULL_REVISION],
647 [67, NULL_REVISION],
777 [67, FAKE_NULL_REVISION],
648 [68, NULL_REVISION],
778 [68, FAKE_NULL_REVISION],
649 [37, 28],
779 [37, 28],
650 [69, 25],
780 [69, 25],
651 [71, NULL_REVISION],
781 [71, FAKE_NULL_REVISION],
652 [72, NULL_REVISION],
782 [72, FAKE_NULL_REVISION],
653 [50, 2],
783 [50, 2],
654 [74, NULL_REVISION],
784 [74, FAKE_NULL_REVISION],
655 [12, NULL_REVISION],
785 [12, FAKE_NULL_REVISION],
656 [18, NULL_REVISION],
786 [18, FAKE_NULL_REVISION],
657 [77, NULL_REVISION],
787 [77, FAKE_NULL_REVISION],
658 [78, NULL_REVISION],
788 [78, FAKE_NULL_REVISION],
659 [79, NULL_REVISION],
789 [79, FAKE_NULL_REVISION],
660 [43, 33],
790 [43, 33],
661 [81, NULL_REVISION],
791 [81, FAKE_NULL_REVISION],
662 [82, NULL_REVISION],
792 [82, FAKE_NULL_REVISION],
663 [83, NULL_REVISION],
793 [83, FAKE_NULL_REVISION],
664 [84, 45],
794 [84, 45],
665 [85, NULL_REVISION],
795 [85, FAKE_NULL_REVISION],
666 [86, NULL_REVISION],
796 [86, FAKE_NULL_REVISION],
667 [NULL_REVISION, NULL_REVISION],
797 [FAKE_NULL_REVISION, FAKE_NULL_REVISION],
668 [88, NULL_REVISION],
798 [88, FAKE_NULL_REVISION],
669 [NULL_REVISION, NULL_REVISION],
799 [FAKE_NULL_REVISION, FAKE_NULL_REVISION],
670 [76, 83],
800 [76, 83],
671 [44, NULL_REVISION],
801 [44, FAKE_NULL_REVISION],
672 [92, NULL_REVISION],
802 [92, FAKE_NULL_REVISION],
673 [93, NULL_REVISION],
803 [93, FAKE_NULL_REVISION],
674 [9, NULL_REVISION],
804 [9, FAKE_NULL_REVISION],
675 [95, 67],
805 [95, 67],
676 [96, NULL_REVISION],
806 [96, FAKE_NULL_REVISION],
677 [97, NULL_REVISION],
807 [97, FAKE_NULL_REVISION],
678 [NULL_REVISION, NULL_REVISION],
808 [FAKE_NULL_REVISION, FAKE_NULL_REVISION],
679 ];
809 ]
680 let problem_rev = 28 as Revision;
810 .into_iter()
681 let problem_base = 70 as Revision;
811 .map(|[a, b]| [Revision(a), Revision(b)])
812 .collect();
813 let problem_rev = 28.into();
814 let problem_base = 70.into();
682 // making the problem obvious: problem_rev is a parent of problem_base
815 // making the problem obvious: problem_rev is a parent of problem_base
683 assert_eq!(graph.parents(problem_base).unwrap()[1], problem_rev);
816 assert_eq!(graph.parents(problem_base).unwrap()[1], problem_rev);
684
817
@@ -687,14 +820,14 b' mod tests {'
687 graph,
820 graph,
688 [60, 26, 70, 3, 96, 19, 98, 49, 97, 47, 1, 6]
821 [60, 26, 70, 3, 96, 19, 98, 49, 97, 47, 1, 6]
689 .iter()
822 .iter()
690 .cloned(),
823 .map(|r| Revision(*r)),
691 );
824 );
692 assert!(missing_ancestors.bases.contains(&problem_base));
825 assert!(missing_ancestors.bases.contains(&problem_base));
693
826
694 let mut revs: HashSet<Revision> =
827 let mut revs: HashSet<Revision> =
695 [4, 12, 41, 28, 68, 38, 1, 30, 56, 44]
828 [4, 12, 41, 28, 68, 38, 1, 30, 56, 44]
696 .iter()
829 .iter()
697 .cloned()
830 .map(|r| Revision(*r))
698 .collect();
831 .collect();
699 missing_ancestors.remove_ancestors_from(&mut revs).unwrap();
832 missing_ancestors.remove_ancestors_from(&mut revs).unwrap();
700 assert!(!revs.contains(&problem_rev));
833 assert!(!revs.contains(&problem_rev));
@@ -1,5 +1,12 b''
1 use super::*;
1 use super::*;
2
2
3 /// Shorthand to reduce boilerplate when creating [`Revision`] for testing
4 macro_rules! R {
5 ($revision:literal) => {
6 Revision($revision)
7 };
8 }
9
3 /// Unit tests for:
10 /// Unit tests for:
4 ///
11 ///
5 /// ```ignore
12 /// ```ignore
@@ -27,7 +34,12 b' fn test_compare_value() {'
27 use MergePick::*;
34 use MergePick::*;
28
35
29 assert_eq!(
36 assert_eq!(
30 compare_value!(1, Normal, (1, None, { 1 }), (1, None, { 1 })),
37 compare_value!(
38 R!(1),
39 Normal,
40 (R!(1), None, { R!(1) }),
41 (R!(1), None, { R!(1) })
42 ),
31 (Any, false)
43 (Any, false)
32 );
44 );
33 }
45 }
@@ -70,12 +82,12 b' fn test_merge_copies_dict() {'
70
82
71 assert_eq!(
83 assert_eq!(
72 merge_copies_dict!(
84 merge_copies_dict!(
73 1,
85 R!(1),
74 {"foo" => (1, None, {})},
86 {"foo" => (R!(1), None, {})},
75 {},
87 {},
76 {"foo" => Merged}
88 {"foo" => Merged}
77 ),
89 ),
78 internal_path_copies!("foo" => (1, None, {}))
90 internal_path_copies!("foo" => (R!(1), None, {}))
79 );
91 );
80 }
92 }
81
93
@@ -124,17 +136,29 b' fn test_combine_changeset_copies() {'
124
136
125 assert_eq!(
137 assert_eq!(
126 combine_changeset_copies!(
138 combine_changeset_copies!(
127 { 1 => 1, 2 => 1 },
139 { R!(1) => 1, R!(2) => 1 },
128 [
140 [
129 { rev: 1, p1: NULL, p2: NULL, actions: [], merge_cases: {}, },
141 {
130 { rev: 2, p1: NULL, p2: NULL, actions: [], merge_cases: {}, },
142 rev: R!(1),
143 p1: NULL,
144 p2: NULL,
145 actions: [],
146 merge_cases: {},
147 },
131 {
148 {
132 rev: 3, p1: 1, p2: 2,
149 rev: R!(2),
150 p1: NULL,
151 p2: NULL,
152 actions: [],
153 merge_cases: {},
154 },
155 {
156 rev: R!(3), p1: R!(1), p2: R!(2),
133 actions: [CopiedFromP1("destination.txt", "source.txt")],
157 actions: [CopiedFromP1("destination.txt", "source.txt")],
134 merge_cases: {"destination.txt" => Merged},
158 merge_cases: {"destination.txt" => Merged},
135 },
159 },
136 ],
160 ],
137 3,
161 R!(3),
138 ),
162 ),
139 path_copies!("destination.txt" => "source.txt")
163 path_copies!("destination.txt" => "source.txt")
140 );
164 );
@@ -171,14 +171,15 b' pub fn range('
171 mod tests {
171 mod tests {
172
172
173 use super::*;
173 use super::*;
174 use crate::testing::SampleGraph;
174 use crate::{testing::SampleGraph, BaseRevision};
175
175
176 /// Apply `retain_heads()` to the given slice and return as a sorted `Vec`
176 /// Apply `retain_heads()` to the given slice and return as a sorted `Vec`
177 fn retain_heads_sorted(
177 fn retain_heads_sorted(
178 graph: &impl Graph,
178 graph: &impl Graph,
179 revs: &[Revision],
179 revs: &[BaseRevision],
180 ) -> Result<Vec<Revision>, GraphError> {
180 ) -> Result<Vec<Revision>, GraphError> {
181 let mut revs: HashSet<Revision> = revs.iter().cloned().collect();
181 let mut revs: HashSet<Revision> =
182 revs.iter().cloned().map(Revision).collect();
182 retain_heads(graph, &mut revs)?;
183 retain_heads(graph, &mut revs)?;
183 let mut as_vec: Vec<Revision> = revs.iter().cloned().collect();
184 let mut as_vec: Vec<Revision> = revs.iter().cloned().collect();
184 as_vec.sort_unstable();
185 as_vec.sort_unstable();
@@ -202,9 +203,11 b' mod tests {'
202 /// Apply `heads()` to the given slice and return as a sorted `Vec`
203 /// Apply `heads()` to the given slice and return as a sorted `Vec`
203 fn heads_sorted(
204 fn heads_sorted(
204 graph: &impl Graph,
205 graph: &impl Graph,
205 revs: &[Revision],
206 revs: &[BaseRevision],
206 ) -> Result<Vec<Revision>, GraphError> {
207 ) -> Result<Vec<Revision>, GraphError> {
207 let heads = heads(graph, revs.iter())?;
208 let iter_revs: Vec<_> =
209 revs.into_iter().cloned().map(Revision).collect();
210 let heads = heads(graph, iter_revs.iter())?;
208 let mut as_vec: Vec<Revision> = heads.iter().cloned().collect();
211 let mut as_vec: Vec<Revision> = heads.iter().cloned().collect();
209 as_vec.sort_unstable();
212 as_vec.sort_unstable();
210 Ok(as_vec)
213 Ok(as_vec)
@@ -227,9 +230,9 b' mod tests {'
227 /// Apply `roots()` and sort the result for easier comparison
230 /// Apply `roots()` and sort the result for easier comparison
228 fn roots_sorted(
231 fn roots_sorted(
229 graph: &impl Graph,
232 graph: &impl Graph,
230 revs: &[Revision],
233 revs: &[BaseRevision],
231 ) -> Result<Vec<Revision>, GraphError> {
234 ) -> Result<Vec<Revision>, GraphError> {
232 let set: HashSet<_> = revs.iter().cloned().collect();
235 let set: HashSet<_> = revs.iter().cloned().map(Revision).collect();
233 let mut as_vec = roots(graph, &set)?;
236 let mut as_vec = roots(graph, &set)?;
234 as_vec.sort_unstable();
237 as_vec.sort_unstable();
235 Ok(as_vec)
238 Ok(as_vec)
@@ -252,17 +255,24 b' mod tests {'
252 /// Apply `range()` and convert the result into a Vec for easier comparison
255 /// Apply `range()` and convert the result into a Vec for easier comparison
253 fn range_vec(
256 fn range_vec(
254 graph: impl Graph + Clone,
257 graph: impl Graph + Clone,
255 roots: &[Revision],
258 roots: &[BaseRevision],
256 heads: &[Revision],
259 heads: &[BaseRevision],
257 ) -> Result<Vec<Revision>, GraphError> {
260 ) -> Result<Vec<Revision>, GraphError> {
258 range(&graph, roots.iter().cloned(), heads.iter().cloned())
261 range(
259 .map(|bs| bs.into_iter().collect())
262 &graph,
263 roots.iter().cloned().map(Revision),
264 heads.iter().cloned().map(Revision),
265 )
266 .map(|bs| bs.into_iter().collect())
260 }
267 }
261
268
262 #[test]
269 #[test]
263 fn test_range() -> Result<(), GraphError> {
270 fn test_range() -> Result<(), GraphError> {
264 assert_eq!(range_vec(SampleGraph, &[0], &[4])?, vec![0, 1, 2, 4]);
271 assert_eq!(range_vec(SampleGraph, &[0], &[4])?, vec![0, 1, 2, 4]);
265 assert_eq!(range_vec(SampleGraph, &[0], &[8])?, vec![]);
272 assert_eq!(
273 range_vec(SampleGraph, &[0], &[8])?,
274 Vec::<Revision>::new()
275 );
266 assert_eq!(
276 assert_eq!(
267 range_vec(SampleGraph, &[5, 6], &[10, 11, 13])?,
277 range_vec(SampleGraph, &[5, 6], &[10, 11, 13])?,
268 vec![5, 10]
278 vec![5, 10]
@@ -481,6 +481,13 b' mod tests {'
481 use super::*;
481 use super::*;
482 use crate::testing::SampleGraph;
482 use crate::testing::SampleGraph;
483
483
484 /// Shorthand to reduce boilerplate when creating [`Revision`] for testing
485 macro_rules! R {
486 ($revision:literal) => {
487 Revision($revision)
488 };
489 }
490
484 /// A PartialDiscovery as for pushing all the heads of `SampleGraph`
491 /// A PartialDiscovery as for pushing all the heads of `SampleGraph`
485 ///
492 ///
486 /// To avoid actual randomness in these tests, we give it a fixed
493 /// To avoid actual randomness in these tests, we give it a fixed
@@ -488,7 +495,7 b' mod tests {'
488 fn full_disco() -> PartialDiscovery<SampleGraph> {
495 fn full_disco() -> PartialDiscovery<SampleGraph> {
489 PartialDiscovery::new_with_seed(
496 PartialDiscovery::new_with_seed(
490 SampleGraph,
497 SampleGraph,
491 vec![10, 11, 12, 13],
498 vec![R!(10), R!(11), R!(12), R!(13)],
492 [0; 16],
499 [0; 16],
493 true,
500 true,
494 true,
501 true,
@@ -501,7 +508,7 b' mod tests {'
501 fn disco12() -> PartialDiscovery<SampleGraph> {
508 fn disco12() -> PartialDiscovery<SampleGraph> {
502 PartialDiscovery::new_with_seed(
509 PartialDiscovery::new_with_seed(
503 SampleGraph,
510 SampleGraph,
504 vec![12],
511 vec![R!(12)],
505 [0; 16],
512 [0; 16],
506 true,
513 true,
507 true,
514 true,
@@ -540,7 +547,7 b' mod tests {'
540 assert!(!disco.has_info());
547 assert!(!disco.has_info());
541 assert_eq!(disco.stats().undecided, None);
548 assert_eq!(disco.stats().undecided, None);
542
549
543 disco.add_common_revisions(vec![11, 12])?;
550 disco.add_common_revisions(vec![R!(11), R!(12)])?;
544 assert!(disco.has_info());
551 assert!(disco.has_info());
545 assert!(!disco.is_complete());
552 assert!(!disco.is_complete());
546 assert!(disco.missing.is_empty());
553 assert!(disco.missing.is_empty());
@@ -559,14 +566,14 b' mod tests {'
559 #[test]
566 #[test]
560 fn test_discovery() -> Result<(), GraphError> {
567 fn test_discovery() -> Result<(), GraphError> {
561 let mut disco = full_disco();
568 let mut disco = full_disco();
562 disco.add_common_revisions(vec![11, 12])?;
569 disco.add_common_revisions(vec![R!(11), R!(12)])?;
563 disco.add_missing_revisions(vec![8, 10])?;
570 disco.add_missing_revisions(vec![R!(8), R!(10)])?;
564 assert_eq!(sorted_undecided(&disco), vec![5]);
571 assert_eq!(sorted_undecided(&disco), vec![5]);
565 assert_eq!(sorted_missing(&disco), vec![8, 10, 13]);
572 assert_eq!(sorted_missing(&disco), vec![8, 10, 13]);
566 assert!(!disco.is_complete());
573 assert!(!disco.is_complete());
567
574
568 disco.add_common_revisions(vec![5])?;
575 disco.add_common_revisions(vec![R!(5)])?;
569 assert_eq!(sorted_undecided(&disco), vec![]);
576 assert_eq!(sorted_undecided(&disco), Vec::<Revision>::new());
570 assert_eq!(sorted_missing(&disco), vec![8, 10, 13]);
577 assert_eq!(sorted_missing(&disco), vec![8, 10, 13]);
571 assert!(disco.is_complete());
578 assert!(disco.is_complete());
572 assert_eq!(sorted_common_heads(&disco)?, vec![5, 11, 12]);
579 assert_eq!(sorted_common_heads(&disco)?, vec![5, 11, 12]);
@@ -577,12 +584,12 b' mod tests {'
577 fn test_add_missing_early_continue() -> Result<(), GraphError> {
584 fn test_add_missing_early_continue() -> Result<(), GraphError> {
578 eprintln!("test_add_missing_early_stop");
585 eprintln!("test_add_missing_early_stop");
579 let mut disco = full_disco();
586 let mut disco = full_disco();
580 disco.add_common_revisions(vec![13, 3, 4])?;
587 disco.add_common_revisions(vec![R!(13), R!(3), R!(4)])?;
581 disco.ensure_children_cache()?;
588 disco.ensure_children_cache()?;
582 // 12 is grand-child of 6 through 9
589 // 12 is grand-child of 6 through 9
583 // passing them in this order maximizes the chances of the
590 // passing them in this order maximizes the chances of the
584 // early continue to do the wrong thing
591 // early continue to do the wrong thing
585 disco.add_missing_revisions(vec![6, 9, 12])?;
592 disco.add_missing_revisions(vec![R!(6), R!(9), R!(12)])?;
586 assert_eq!(sorted_undecided(&disco), vec![5, 7, 10, 11]);
593 assert_eq!(sorted_undecided(&disco), vec![5, 7, 10, 11]);
587 assert_eq!(sorted_missing(&disco), vec![6, 9, 12]);
594 assert_eq!(sorted_missing(&disco), vec![6, 9, 12]);
588 assert!(!disco.is_complete());
595 assert!(!disco.is_complete());
@@ -591,18 +598,24 b' mod tests {'
591
598
592 #[test]
599 #[test]
593 fn test_limit_sample_no_need_to() {
600 fn test_limit_sample_no_need_to() {
594 let sample = vec![1, 2, 3, 4];
601 let sample = vec![R!(1), R!(2), R!(3), R!(4)];
595 assert_eq!(full_disco().limit_sample(sample, 10), vec![1, 2, 3, 4]);
602 assert_eq!(full_disco().limit_sample(sample, 10), vec![1, 2, 3, 4]);
596 }
603 }
597
604
598 #[test]
605 #[test]
599 fn test_limit_sample_less_than_half() {
606 fn test_limit_sample_less_than_half() {
600 assert_eq!(full_disco().limit_sample((1..6).collect(), 2), vec![2, 5]);
607 assert_eq!(
608 full_disco().limit_sample((1..6).map(Revision).collect(), 2),
609 vec![2, 5]
610 );
601 }
611 }
602
612
603 #[test]
613 #[test]
604 fn test_limit_sample_more_than_half() {
614 fn test_limit_sample_more_than_half() {
605 assert_eq!(full_disco().limit_sample((1..4).collect(), 2), vec![1, 2]);
615 assert_eq!(
616 full_disco().limit_sample((1..4).map(Revision).collect(), 2),
617 vec![1, 2]
618 );
606 }
619 }
607
620
608 #[test]
621 #[test]
@@ -610,7 +623,10 b' mod tests {'
610 let mut disco = full_disco();
623 let mut disco = full_disco();
611 disco.randomize = false;
624 disco.randomize = false;
612 assert_eq!(
625 assert_eq!(
613 disco.limit_sample(vec![1, 8, 13, 5, 7, 3], 4),
626 disco.limit_sample(
627 vec![R!(1), R!(8), R!(13), R!(5), R!(7), R!(3)],
628 4
629 ),
614 vec![1, 3, 5, 7]
630 vec![1, 3, 5, 7]
615 );
631 );
616 }
632 }
@@ -618,7 +634,7 b' mod tests {'
618 #[test]
634 #[test]
619 fn test_quick_sample_enough_undecided_heads() -> Result<(), GraphError> {
635 fn test_quick_sample_enough_undecided_heads() -> Result<(), GraphError> {
620 let mut disco = full_disco();
636 let mut disco = full_disco();
621 disco.undecided = Some((1..=13).collect());
637 disco.undecided = Some((1..=13).map(Revision).collect());
622
638
623 let mut sample_vec = disco.take_quick_sample(vec![], 4)?;
639 let mut sample_vec = disco.take_quick_sample(vec![], 4)?;
624 sample_vec.sort_unstable();
640 sample_vec.sort_unstable();
@@ -631,7 +647,7 b' mod tests {'
631 let mut disco = disco12();
647 let mut disco = disco12();
632 disco.ensure_undecided()?;
648 disco.ensure_undecided()?;
633
649
634 let mut sample_vec = disco.take_quick_sample(vec![12], 4)?;
650 let mut sample_vec = disco.take_quick_sample(vec![R!(12)], 4)?;
635 sample_vec.sort_unstable();
651 sample_vec.sort_unstable();
636 // r12's only parent is r9, whose unique grand-parent through the
652 // r12's only parent is r9, whose unique grand-parent through the
637 // diamond shape is r4. This ends there because the distance from r4
653 // diamond shape is r4. This ends there because the distance from r4
@@ -646,16 +662,16 b' mod tests {'
646 disco.ensure_children_cache()?;
662 disco.ensure_children_cache()?;
647
663
648 let cache = disco.children_cache.unwrap();
664 let cache = disco.children_cache.unwrap();
649 assert_eq!(cache.get(&2).cloned(), Some(vec![4]));
665 assert_eq!(cache.get(&R!(2)).cloned(), Some(vec![R!(4)]));
650 assert_eq!(cache.get(&10).cloned(), None);
666 assert_eq!(cache.get(&R!(10)).cloned(), None);
651
667
652 let mut children_4 = cache.get(&4).cloned().unwrap();
668 let mut children_4 = cache.get(&R!(4)).cloned().unwrap();
653 children_4.sort_unstable();
669 children_4.sort_unstable();
654 assert_eq!(children_4, vec![5, 6, 7]);
670 assert_eq!(children_4, vec![R!(5), R!(6), R!(7)]);
655
671
656 let mut children_7 = cache.get(&7).cloned().unwrap();
672 let mut children_7 = cache.get(&R!(7)).cloned().unwrap();
657 children_7.sort_unstable();
673 children_7.sort_unstable();
658 assert_eq!(children_7, vec![9, 11]);
674 assert_eq!(children_7, vec![R!(9), R!(11)]);
659
675
660 Ok(())
676 Ok(())
661 }
677 }
@@ -664,14 +680,14 b' mod tests {'
664 fn test_complete_sample() {
680 fn test_complete_sample() {
665 let mut disco = full_disco();
681 let mut disco = full_disco();
666 let undecided: HashSet<Revision> =
682 let undecided: HashSet<Revision> =
667 [4, 7, 9, 2, 3].iter().cloned().collect();
683 [4, 7, 9, 2, 3].iter().cloned().map(Revision).collect();
668 disco.undecided = Some(undecided);
684 disco.undecided = Some(undecided);
669
685
670 let mut sample = vec![0];
686 let mut sample = vec![R!(0)];
671 disco.random_complete_sample(&mut sample, 3);
687 disco.random_complete_sample(&mut sample, 3);
672 assert_eq!(sample.len(), 3);
688 assert_eq!(sample.len(), 3);
673
689
674 let mut sample = vec![2, 4, 7];
690 let mut sample = vec![R!(2), R!(4), R!(7)];
675 disco.random_complete_sample(&mut sample, 1);
691 disco.random_complete_sample(&mut sample, 1);
676 assert_eq!(sample.len(), 3);
692 assert_eq!(sample.len(), 3);
677 }
693 }
@@ -679,7 +695,7 b' mod tests {'
679 #[test]
695 #[test]
680 fn test_bidirectional_sample() -> Result<(), GraphError> {
696 fn test_bidirectional_sample() -> Result<(), GraphError> {
681 let mut disco = full_disco();
697 let mut disco = full_disco();
682 disco.undecided = Some((0..=13).into_iter().collect());
698 disco.undecided = Some((0..=13).into_iter().map(Revision).collect());
683
699
684 let (sample_set, size) = disco.bidirectional_sample(7)?;
700 let (sample_set, size) = disco.bidirectional_sample(7)?;
685 assert_eq!(size, 7);
701 assert_eq!(size, 7);
@@ -215,7 +215,7 b' impl Index {'
215 rev: Revision,
215 rev: Revision,
216 offsets: &[usize],
216 offsets: &[usize],
217 ) -> IndexEntry {
217 ) -> IndexEntry {
218 let start = offsets[rev as usize];
218 let start = offsets[rev.0 as usize];
219 let end = start + INDEX_ENTRY_SIZE;
219 let end = start + INDEX_ENTRY_SIZE;
220 let bytes = &self.bytes[start..end];
220 let bytes = &self.bytes[start..end];
221
221
@@ -229,13 +229,13 b' impl Index {'
229 }
229 }
230
230
231 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
231 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
232 let start = rev as usize * INDEX_ENTRY_SIZE;
232 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
233 let end = start + INDEX_ENTRY_SIZE;
233 let end = start + INDEX_ENTRY_SIZE;
234 let bytes = &self.bytes[start..end];
234 let bytes = &self.bytes[start..end];
235
235
236 // Override the offset of the first revision as its bytes are used
236 // Override the offset of the first revision as its bytes are used
237 // for the index's metadata (saving space because it is always 0)
237 // for the index's metadata (saving space because it is always 0)
238 let offset_override = if rev == 0 { Some(0) } else { None };
238 let offset_override = if rev == Revision(0) { Some(0) } else { None };
239
239
240 IndexEntry {
240 IndexEntry {
241 bytes,
241 bytes,
@@ -359,8 +359,8 b' mod tests {'
359 offset: 0,
359 offset: 0,
360 compressed_len: 0,
360 compressed_len: 0,
361 uncompressed_len: 0,
361 uncompressed_len: 0,
362 base_revision_or_base_of_delta_chain: 0,
362 base_revision_or_base_of_delta_chain: Revision(0),
363 link_revision: 0,
363 link_revision: Revision(0),
364 p1: NULL_REVISION,
364 p1: NULL_REVISION,
365 p2: NULL_REVISION,
365 p2: NULL_REVISION,
366 node: NULL_NODE,
366 node: NULL_NODE,
@@ -450,11 +450,11 b' mod tests {'
450 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
450 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
451 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
451 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
452 bytes.extend(
452 bytes.extend(
453 &self.base_revision_or_base_of_delta_chain.to_be_bytes(),
453 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
454 );
454 );
455 bytes.extend(&self.link_revision.to_be_bytes());
455 bytes.extend(&self.link_revision.0.to_be_bytes());
456 bytes.extend(&self.p1.to_be_bytes());
456 bytes.extend(&self.p1.0.to_be_bytes());
457 bytes.extend(&self.p2.to_be_bytes());
457 bytes.extend(&self.p2.0.to_be_bytes());
458 bytes.extend(self.node.as_bytes());
458 bytes.extend(self.node.as_bytes());
459 bytes.extend(vec![0u8; 12]);
459 bytes.extend(vec![0u8; 12]);
460 bytes
460 bytes
@@ -564,7 +564,7 b' mod tests {'
564 #[test]
564 #[test]
565 fn test_base_revision_or_base_of_delta_chain() {
565 fn test_base_revision_or_base_of_delta_chain() {
566 let bytes = IndexEntryBuilder::new()
566 let bytes = IndexEntryBuilder::new()
567 .with_base_revision_or_base_of_delta_chain(1)
567 .with_base_revision_or_base_of_delta_chain(Revision(1))
568 .build();
568 .build();
569 let entry = IndexEntry {
569 let entry = IndexEntry {
570 bytes: &bytes,
570 bytes: &bytes,
@@ -576,7 +576,9 b' mod tests {'
576
576
577 #[test]
577 #[test]
578 fn link_revision_test() {
578 fn link_revision_test() {
579 let bytes = IndexEntryBuilder::new().with_link_revision(123).build();
579 let bytes = IndexEntryBuilder::new()
580 .with_link_revision(Revision(123))
581 .build();
580
582
581 let entry = IndexEntry {
583 let entry = IndexEntry {
582 bytes: &bytes,
584 bytes: &bytes,
@@ -588,7 +590,7 b' mod tests {'
588
590
589 #[test]
591 #[test]
590 fn p1_test() {
592 fn p1_test() {
591 let bytes = IndexEntryBuilder::new().with_p1(123).build();
593 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
592
594
593 let entry = IndexEntry {
595 let entry = IndexEntry {
594 bytes: &bytes,
596 bytes: &bytes,
@@ -600,7 +602,7 b' mod tests {'
600
602
601 #[test]
603 #[test]
602 fn p2_test() {
604 fn p2_test() {
603 let bytes = IndexEntryBuilder::new().with_p2(123).build();
605 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
604
606
605 let entry = IndexEntry {
607 let entry = IndexEntry {
606 bytes: &bytes,
608 bytes: &bytes,
@@ -33,20 +33,14 b' use super::nodemap::{NodeMap, NodeMapErr'
33 use crate::errors::HgError;
33 use crate::errors::HgError;
34 use crate::vfs::Vfs;
34 use crate::vfs::Vfs;
35
35
36 /// Mercurial revision numbers
37 ///
38 /// As noted in revlog.c, revision numbers are actually encoded in
36 /// As noted in revlog.c, revision numbers are actually encoded in
39 /// 4 bytes, and are liberally converted to ints, whence the i32
37 /// 4 bytes, and are liberally converted to ints, whence the i32
40 pub type Revision = i32;
38 pub type BaseRevision = i32;
41
39
42 /// Unchecked Mercurial revision numbers.
40 /// Mercurial revision numbers
43 ///
41 /// In contrast to the more general [`UncheckedRevision`], these are "checked"
44 /// Values of this type have no guarantee of being a valid revision number
42 /// in the sense that they should only be used for revisions that are
45 /// in any context. Use method `check_revision` to get a valid revision within
43 /// valid for a given index (i.e. in bounds).
46 /// the appropriate index object.
47 ///
48 /// As noted in revlog.c, revision numbers are actually encoded in
49 /// 4 bytes, and are liberally converted to ints, whence the i32
50 #[derive(
44 #[derive(
51 Debug,
45 Debug,
52 derive_more::Display,
46 derive_more::Display,
@@ -58,10 +52,52 b' pub type Revision = i32;'
58 PartialOrd,
52 PartialOrd,
59 Ord,
53 Ord,
60 )]
54 )]
61 pub struct UncheckedRevision(i32);
55 pub struct Revision(pub BaseRevision);
56
57 impl format_bytes::DisplayBytes for Revision {
58 fn display_bytes(
59 &self,
60 output: &mut dyn std::io::Write,
61 ) -> std::io::Result<()> {
62 self.0.display_bytes(output)
63 }
64 }
65
66 /// Unchecked Mercurial revision numbers.
67 ///
68 /// Values of this type have no guarantee of being a valid revision number
69 /// in any context. Use method `check_revision` to get a valid revision within
70 /// the appropriate index object.
71 #[derive(
72 Debug,
73 derive_more::Display,
74 Clone,
75 Copy,
76 Hash,
77 PartialEq,
78 Eq,
79 PartialOrd,
80 Ord,
81 )]
82 pub struct UncheckedRevision(pub BaseRevision);
83
84 impl format_bytes::DisplayBytes for UncheckedRevision {
85 fn display_bytes(
86 &self,
87 output: &mut dyn std::io::Write,
88 ) -> std::io::Result<()> {
89 self.0.display_bytes(output)
90 }
91 }
62
92
63 impl From<Revision> for UncheckedRevision {
93 impl From<Revision> for UncheckedRevision {
64 fn from(value: Revision) -> Self {
94 fn from(value: Revision) -> Self {
95 Self(value.0)
96 }
97 }
98
99 impl From<BaseRevision> for UncheckedRevision {
100 fn from(value: BaseRevision) -> Self {
65 Self(value)
101 Self(value)
66 }
102 }
67 }
103 }
@@ -70,7 +106,7 b' impl From<Revision> for UncheckedRevisio'
70 ///
106 ///
71 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
107 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
72 /// to be smaller than all existing revisions.
108 /// to be smaller than all existing revisions.
73 pub const NULL_REVISION: Revision = -1;
109 pub const NULL_REVISION: Revision = Revision(-1);
74
110
75 /// Same as `mercurial.node.wdirrev`
111 /// Same as `mercurial.node.wdirrev`
76 ///
112 ///
@@ -116,8 +152,9 b' pub trait RevlogIndex {'
116 fn check_revision(&self, rev: UncheckedRevision) -> Option<Revision> {
152 fn check_revision(&self, rev: UncheckedRevision) -> Option<Revision> {
117 let rev = rev.0;
153 let rev = rev.0;
118
154
119 if rev == NULL_REVISION || (rev >= 0 && (rev as usize) < self.len()) {
155 if rev == NULL_REVISION.0 || (rev >= 0 && (rev as usize) < self.len())
120 Some(rev)
156 {
157 Some(Revision(rev))
121 } else {
158 } else {
122 None
159 None
123 }
160 }
@@ -301,7 +338,8 b' impl Revlog {'
301 // TODO: consider building a non-persistent nodemap in memory to
338 // TODO: consider building a non-persistent nodemap in memory to
302 // optimize these cases.
339 // optimize these cases.
303 let mut found_by_prefix = None;
340 let mut found_by_prefix = None;
304 for rev in (0..self.len() as Revision).rev() {
341 for rev in (0..self.len()).rev() {
342 let rev = Revision(rev as BaseRevision);
305 let index_entry = self.index.get_entry(rev).ok_or_else(|| {
343 let index_entry = self.index.get_entry(rev).ok_or_else(|| {
306 HgError::corrupted(
344 HgError::corrupted(
307 "revlog references a revision not in the index",
345 "revlog references a revision not in the index",
@@ -600,7 +638,7 b" impl<'revlog> RevlogEntry<'revlog> {"
600 delta_chain.push(entry);
638 delta_chain.push(entry);
601 self.revlog.get_entry_for_checked_rev(base_rev)?
639 self.revlog.get_entry_for_checked_rev(base_rev)?
602 } else {
640 } else {
603 let base_rev = UncheckedRevision(entry.rev - 1);
641 let base_rev = UncheckedRevision(entry.rev.0 - 1);
604 delta_chain.push(entry);
642 delta_chain.push(entry);
605 self.revlog.get_entry(base_rev)?
643 self.revlog.get_entry(base_rev)?
606 };
644 };
@@ -800,8 +838,8 b' mod tests {'
800 .build();
838 .build();
801 let entry2_bytes = IndexEntryBuilder::new()
839 let entry2_bytes = IndexEntryBuilder::new()
802 .with_offset(INDEX_ENTRY_SIZE)
840 .with_offset(INDEX_ENTRY_SIZE)
803 .with_p1(0)
841 .with_p1(Revision(0))
804 .with_p2(1)
842 .with_p2(Revision(1))
805 .with_node(node2)
843 .with_node(node2)
806 .build();
844 .build();
807 let contents = vec![entry0_bytes, entry1_bytes, entry2_bytes]
845 let contents = vec![entry0_bytes, entry1_bytes, entry2_bytes]
@@ -812,7 +850,7 b' mod tests {'
812 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
850 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
813
851
814 let entry0 = revlog.get_entry(0.into()).ok().unwrap();
852 let entry0 = revlog.get_entry(0.into()).ok().unwrap();
815 assert_eq!(entry0.revision(), 0);
853 assert_eq!(entry0.revision(), Revision(0));
816 assert_eq!(*entry0.node(), node0);
854 assert_eq!(*entry0.node(), node0);
817 assert!(!entry0.has_p1());
855 assert!(!entry0.has_p1());
818 assert_eq!(entry0.p1(), None);
856 assert_eq!(entry0.p1(), None);
@@ -823,7 +861,7 b' mod tests {'
823 assert!(p2_entry.is_none());
861 assert!(p2_entry.is_none());
824
862
825 let entry1 = revlog.get_entry(1.into()).ok().unwrap();
863 let entry1 = revlog.get_entry(1.into()).ok().unwrap();
826 assert_eq!(entry1.revision(), 1);
864 assert_eq!(entry1.revision(), Revision(1));
827 assert_eq!(*entry1.node(), node1);
865 assert_eq!(*entry1.node(), node1);
828 assert!(!entry1.has_p1());
866 assert!(!entry1.has_p1());
829 assert_eq!(entry1.p1(), None);
867 assert_eq!(entry1.p1(), None);
@@ -834,17 +872,17 b' mod tests {'
834 assert!(p2_entry.is_none());
872 assert!(p2_entry.is_none());
835
873
836 let entry2 = revlog.get_entry(2.into()).ok().unwrap();
874 let entry2 = revlog.get_entry(2.into()).ok().unwrap();
837 assert_eq!(entry2.revision(), 2);
875 assert_eq!(entry2.revision(), Revision(2));
838 assert_eq!(*entry2.node(), node2);
876 assert_eq!(*entry2.node(), node2);
839 assert!(entry2.has_p1());
877 assert!(entry2.has_p1());
840 assert_eq!(entry2.p1(), Some(0));
878 assert_eq!(entry2.p1(), Some(Revision(0)));
841 assert_eq!(entry2.p2(), Some(1));
879 assert_eq!(entry2.p2(), Some(Revision(1)));
842 let p1_entry = entry2.p1_entry().unwrap();
880 let p1_entry = entry2.p1_entry().unwrap();
843 assert!(p1_entry.is_some());
881 assert!(p1_entry.is_some());
844 assert_eq!(p1_entry.unwrap().revision(), 0);
882 assert_eq!(p1_entry.unwrap().revision(), Revision(0));
845 let p2_entry = entry2.p2_entry().unwrap();
883 let p2_entry = entry2.p2_entry().unwrap();
846 assert!(p2_entry.is_some());
884 assert!(p2_entry.is_some());
847 assert_eq!(p2_entry.unwrap().revision(), 1);
885 assert_eq!(p2_entry.unwrap().revision(), Revision(1));
848 }
886 }
849
887
850 #[test]
888 #[test]
@@ -880,20 +918,23 b' mod tests {'
880 // accessing the data shows the corruption
918 // accessing the data shows the corruption
881 revlog.get_entry(0.into()).unwrap().data().unwrap_err();
919 revlog.get_entry(0.into()).unwrap().data().unwrap_err();
882
920
883 assert_eq!(revlog.rev_from_node(NULL_NODE.into()).unwrap(), -1);
921 assert_eq!(
884 assert_eq!(revlog.rev_from_node(node0.into()).unwrap(), 0);
922 revlog.rev_from_node(NULL_NODE.into()).unwrap(),
885 assert_eq!(revlog.rev_from_node(node1.into()).unwrap(), 1);
923 Revision(-1)
924 );
925 assert_eq!(revlog.rev_from_node(node0.into()).unwrap(), Revision(0));
926 assert_eq!(revlog.rev_from_node(node1.into()).unwrap(), Revision(1));
886 assert_eq!(
927 assert_eq!(
887 revlog
928 revlog
888 .rev_from_node(NodePrefix::from_hex("000").unwrap())
929 .rev_from_node(NodePrefix::from_hex("000").unwrap())
889 .unwrap(),
930 .unwrap(),
890 -1
931 Revision(-1)
891 );
932 );
892 assert_eq!(
933 assert_eq!(
893 revlog
934 revlog
894 .rev_from_node(NodePrefix::from_hex("b00").unwrap())
935 .rev_from_node(NodePrefix::from_hex("b00").unwrap())
895 .unwrap(),
936 .unwrap(),
896 1
937 Revision(1)
897 );
938 );
898 // RevlogError does not implement PartialEq
939 // RevlogError does not implement PartialEq
899 // (ultimately because io::Error does not)
940 // (ultimately because io::Error does not)
@@ -474,9 +474,12 b' impl NodeTree {'
474 self.mutable_block(deepest.block_idx);
474 self.mutable_block(deepest.block_idx);
475
475
476 if let Element::Rev(old_rev) = deepest.element {
476 if let Element::Rev(old_rev) = deepest.element {
477 let old_node = index.node(old_rev).ok_or_else(|| {
477 let old_node = index
478 NodeMapError::RevisionNotInIndex(old_rev.into())
478 .check_revision(old_rev.into())
479 })?;
479 .and_then(|rev| index.node(rev))
480 .ok_or_else(|| {
481 NodeMapError::RevisionNotInIndex(old_rev.into())
482 })?;
480 if old_node == node {
483 if old_node == node {
481 return Ok(()); // avoid creating lots of useless blocks
484 return Ok(()); // avoid creating lots of useless blocks
482 }
485 }
@@ -500,14 +503,14 b' impl NodeTree {'
500 } else {
503 } else {
501 let mut new_block = Block::new();
504 let mut new_block = Block::new();
502 new_block.set(old_nybble, Element::Rev(old_rev));
505 new_block.set(old_nybble, Element::Rev(old_rev));
503 new_block.set(new_nybble, Element::Rev(rev));
506 new_block.set(new_nybble, Element::Rev(rev.0));
504 self.growable.push(new_block);
507 self.growable.push(new_block);
505 break;
508 break;
506 }
509 }
507 }
510 }
508 } else {
511 } else {
509 // Free slot in the deepest block: no splitting has to be done
512 // Free slot in the deepest block: no splitting has to be done
510 block.set(deepest.nybble, Element::Rev(rev));
513 block.set(deepest.nybble, Element::Rev(rev.0));
511 }
514 }
512
515
513 // Backtrack over visit steps to update references
516 // Backtrack over visit steps to update references
@@ -707,6 +710,13 b' mod tests {'
707 )
710 )
708 }
711 }
709
712
713 /// Shorthand to reduce boilerplate when creating [`Revision`] for testing
714 macro_rules! R {
715 ($revision:literal) => {
716 Revision($revision)
717 };
718 }
719
710 #[test]
720 #[test]
711 fn test_block_debug() {
721 fn test_block_debug() {
712 let mut block = Block::new();
722 let mut block = Block::new();
@@ -755,7 +765,7 b' mod tests {'
755 }
765 }
756
766
757 fn check_revision(&self, rev: UncheckedRevision) -> Option<Revision> {
767 fn check_revision(&self, rev: UncheckedRevision) -> Option<Revision> {
758 self.get(&rev).map(|_| rev.0)
768 self.get(&rev).map(|_| Revision(rev.0))
759 }
769 }
760 }
770 }
761
771
@@ -800,17 +810,20 b' mod tests {'
800 #[test]
810 #[test]
801 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
811 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
802 let mut idx: TestIndex = HashMap::new();
812 let mut idx: TestIndex = HashMap::new();
803 pad_insert(&mut idx, 1, "1234deadcafe");
813 pad_insert(&mut idx, R!(1), "1234deadcafe");
804
814
805 let nt = NodeTree::from(vec![block! {1: Rev(1)}]);
815 let nt = NodeTree::from(vec![block! {1: Rev(1)}]);
806 assert_eq!(nt.find_bin(&idx, hex("1"))?, Some(1));
816 assert_eq!(nt.find_bin(&idx, hex("1"))?, Some(R!(1)));
807 assert_eq!(nt.find_bin(&idx, hex("12"))?, Some(1));
817 assert_eq!(nt.find_bin(&idx, hex("12"))?, Some(R!(1)));
808 assert_eq!(nt.find_bin(&idx, hex("1234de"))?, Some(1));
818 assert_eq!(nt.find_bin(&idx, hex("1234de"))?, Some(R!(1)));
809 assert_eq!(nt.find_bin(&idx, hex("1a"))?, None);
819 assert_eq!(nt.find_bin(&idx, hex("1a"))?, None);
810 assert_eq!(nt.find_bin(&idx, hex("ab"))?, None);
820 assert_eq!(nt.find_bin(&idx, hex("ab"))?, None);
811
821
812 // and with full binary Nodes
822 // and with full binary Nodes
813 assert_eq!(nt.find_node(&idx, idx.get(&1.into()).unwrap())?, Some(1));
823 assert_eq!(
824 nt.find_node(&idx, idx.get(&1.into()).unwrap())?,
825 Some(R!(1))
826 );
814 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
827 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
815 assert_eq!(nt.find_node(&idx, &unknown)?, None);
828 assert_eq!(nt.find_node(&idx, &unknown)?, None);
816 Ok(())
829 Ok(())
@@ -819,15 +832,15 b' mod tests {'
819 #[test]
832 #[test]
820 fn test_immutable_find_one_jump() {
833 fn test_immutable_find_one_jump() {
821 let mut idx = TestIndex::new();
834 let mut idx = TestIndex::new();
822 pad_insert(&mut idx, 9, "012");
835 pad_insert(&mut idx, R!(9), "012");
823 pad_insert(&mut idx, 0, "00a");
836 pad_insert(&mut idx, R!(0), "00a");
824
837
825 let nt = sample_nodetree();
838 let nt = sample_nodetree();
826
839
827 assert_eq!(nt.find_bin(&idx, hex("0")), Err(MultipleResults));
840 assert_eq!(nt.find_bin(&idx, hex("0")), Err(MultipleResults));
828 assert_eq!(nt.find_bin(&idx, hex("01")), Ok(Some(9)));
841 assert_eq!(nt.find_bin(&idx, hex("01")), Ok(Some(R!(9))));
829 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
842 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
830 assert_eq!(nt.find_bin(&idx, hex("00a")), Ok(Some(0)));
843 assert_eq!(nt.find_bin(&idx, hex("00a")), Ok(Some(R!(0))));
831 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("00a")), Ok(Some(3)));
844 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("00a")), Ok(Some(3)));
832 assert_eq!(nt.find_bin(&idx, hex("000")), Ok(Some(NULL_REVISION)));
845 assert_eq!(nt.find_bin(&idx, hex("000")), Ok(Some(NULL_REVISION)));
833 }
846 }
@@ -835,11 +848,11 b' mod tests {'
835 #[test]
848 #[test]
836 fn test_mutated_find() -> Result<(), NodeMapError> {
849 fn test_mutated_find() -> Result<(), NodeMapError> {
837 let mut idx = TestIndex::new();
850 let mut idx = TestIndex::new();
838 pad_insert(&mut idx, 9, "012");
851 pad_insert(&mut idx, R!(9), "012");
839 pad_insert(&mut idx, 0, "00a");
852 pad_insert(&mut idx, R!(0), "00a");
840 pad_insert(&mut idx, 2, "cafe");
853 pad_insert(&mut idx, R!(2), "cafe");
841 pad_insert(&mut idx, 3, "15");
854 pad_insert(&mut idx, R!(3), "15");
842 pad_insert(&mut idx, 1, "10");
855 pad_insert(&mut idx, R!(1), "10");
843
856
844 let nt = NodeTree {
857 let nt = NodeTree {
845 readonly: sample_nodetree().readonly,
858 readonly: sample_nodetree().readonly,
@@ -847,13 +860,13 b' mod tests {'
847 root: block![0: Block(1), 1:Block(3), 12: Rev(2)],
860 root: block![0: Block(1), 1:Block(3), 12: Rev(2)],
848 masked_inner_blocks: 1,
861 masked_inner_blocks: 1,
849 };
862 };
850 assert_eq!(nt.find_bin(&idx, hex("10"))?, Some(1));
863 assert_eq!(nt.find_bin(&idx, hex("10"))?, Some(R!(1)));
851 assert_eq!(nt.find_bin(&idx, hex("c"))?, Some(2));
864 assert_eq!(nt.find_bin(&idx, hex("c"))?, Some(R!(2)));
852 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("c"))?, Some(1));
865 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("c"))?, Some(1));
853 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
866 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
854 assert_eq!(nt.find_bin(&idx, hex("000"))?, Some(NULL_REVISION));
867 assert_eq!(nt.find_bin(&idx, hex("000"))?, Some(NULL_REVISION));
855 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("000"))?, Some(3));
868 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("000"))?, Some(3));
856 assert_eq!(nt.find_bin(&idx, hex("01"))?, Some(9));
869 assert_eq!(nt.find_bin(&idx, hex("01"))?, Some(R!(9)));
857 assert_eq!(nt.masked_readonly_blocks(), 2);
870 assert_eq!(nt.masked_readonly_blocks(), 2);
858 Ok(())
871 Ok(())
859 }
872 }
@@ -915,34 +928,34 b' mod tests {'
915 fn test_insert_full_mutable() -> Result<(), NodeMapError> {
928 fn test_insert_full_mutable() -> Result<(), NodeMapError> {
916 let mut idx = TestNtIndex::new();
929 let mut idx = TestNtIndex::new();
917 idx.insert(0, "1234")?;
930 idx.insert(0, "1234")?;
918 assert_eq!(idx.find_hex("1")?, Some(0));
931 assert_eq!(idx.find_hex("1")?, Some(R!(0)));
919 assert_eq!(idx.find_hex("12")?, Some(0));
932 assert_eq!(idx.find_hex("12")?, Some(R!(0)));
920
933
921 // let's trigger a simple split
934 // let's trigger a simple split
922 idx.insert(1, "1a34")?;
935 idx.insert(1, "1a34")?;
923 assert_eq!(idx.nt.growable.len(), 1);
936 assert_eq!(idx.nt.growable.len(), 1);
924 assert_eq!(idx.find_hex("12")?, Some(0));
937 assert_eq!(idx.find_hex("12")?, Some(R!(0)));
925 assert_eq!(idx.find_hex("1a")?, Some(1));
938 assert_eq!(idx.find_hex("1a")?, Some(R!(1)));
926
939
927 // reinserting is a no_op
940 // reinserting is a no_op
928 idx.insert(1, "1a34")?;
941 idx.insert(1, "1a34")?;
929 assert_eq!(idx.nt.growable.len(), 1);
942 assert_eq!(idx.nt.growable.len(), 1);
930 assert_eq!(idx.find_hex("12")?, Some(0));
943 assert_eq!(idx.find_hex("12")?, Some(R!(0)));
931 assert_eq!(idx.find_hex("1a")?, Some(1));
944 assert_eq!(idx.find_hex("1a")?, Some(R!(1)));
932
945
933 idx.insert(2, "1a01")?;
946 idx.insert(2, "1a01")?;
934 assert_eq!(idx.nt.growable.len(), 2);
947 assert_eq!(idx.nt.growable.len(), 2);
935 assert_eq!(idx.find_hex("1a"), Err(NodeMapError::MultipleResults));
948 assert_eq!(idx.find_hex("1a"), Err(NodeMapError::MultipleResults));
936 assert_eq!(idx.find_hex("12")?, Some(0));
949 assert_eq!(idx.find_hex("12")?, Some(R!(0)));
937 assert_eq!(idx.find_hex("1a3")?, Some(1));
950 assert_eq!(idx.find_hex("1a3")?, Some(R!(1)));
938 assert_eq!(idx.find_hex("1a0")?, Some(2));
951 assert_eq!(idx.find_hex("1a0")?, Some(R!(2)));
939 assert_eq!(idx.find_hex("1a12")?, None);
952 assert_eq!(idx.find_hex("1a12")?, None);
940
953
941 // now let's make it split and create more than one additional block
954 // now let's make it split and create more than one additional block
942 idx.insert(3, "1a345")?;
955 idx.insert(3, "1a345")?;
943 assert_eq!(idx.nt.growable.len(), 4);
956 assert_eq!(idx.nt.growable.len(), 4);
944 assert_eq!(idx.find_hex("1a340")?, Some(1));
957 assert_eq!(idx.find_hex("1a340")?, Some(R!(1)));
945 assert_eq!(idx.find_hex("1a345")?, Some(3));
958 assert_eq!(idx.find_hex("1a345")?, Some(R!(3)));
946 assert_eq!(idx.find_hex("1a341")?, None);
959 assert_eq!(idx.find_hex("1a341")?, None);
947
960
948 // there's no readonly block to mask
961 // there's no readonly block to mask
@@ -987,12 +1000,12 b' mod tests {'
987 let node1 = Node::from_hex(&node1_hex).unwrap();
1000 let node1 = Node::from_hex(&node1_hex).unwrap();
988
1001
989 idx.insert(0.into(), node0);
1002 idx.insert(0.into(), node0);
990 nt.insert(idx, &node0, 0)?;
1003 nt.insert(idx, &node0, R!(0))?;
991 idx.insert(1.into(), node1);
1004 idx.insert(1.into(), node1);
992 nt.insert(idx, &node1, 1)?;
1005 nt.insert(idx, &node1, R!(1))?;
993
1006
994 assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(0));
1007 assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(R!(0)));
995 assert_eq!(nt.find_bin(idx, (&node1).into())?, Some(1));
1008 assert_eq!(nt.find_bin(idx, (&node1).into())?, Some(R!(1)));
996 Ok(())
1009 Ok(())
997 }
1010 }
998
1011
@@ -1004,28 +1017,28 b' mod tests {'
1004 idx.insert(2, "131")?;
1017 idx.insert(2, "131")?;
1005 idx.insert(3, "cafe")?;
1018 idx.insert(3, "cafe")?;
1006 let mut idx = idx.commit();
1019 let mut idx = idx.commit();
1007 assert_eq!(idx.find_hex("1234")?, Some(0));
1020 assert_eq!(idx.find_hex("1234")?, Some(R!(0)));
1008 assert_eq!(idx.find_hex("1235")?, Some(1));
1021 assert_eq!(idx.find_hex("1235")?, Some(R!(1)));
1009 assert_eq!(idx.find_hex("131")?, Some(2));
1022 assert_eq!(idx.find_hex("131")?, Some(R!(2)));
1010 assert_eq!(idx.find_hex("cafe")?, Some(3));
1023 assert_eq!(idx.find_hex("cafe")?, Some(R!(3)));
1011 // we did not add anything since init from readonly
1024 // we did not add anything since init from readonly
1012 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
1025 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
1013
1026
1014 idx.insert(4, "123A")?;
1027 idx.insert(4, "123A")?;
1015 assert_eq!(idx.find_hex("1234")?, Some(0));
1028 assert_eq!(idx.find_hex("1234")?, Some(R!(0)));
1016 assert_eq!(idx.find_hex("1235")?, Some(1));
1029 assert_eq!(idx.find_hex("1235")?, Some(R!(1)));
1017 assert_eq!(idx.find_hex("131")?, Some(2));
1030 assert_eq!(idx.find_hex("131")?, Some(R!(2)));
1018 assert_eq!(idx.find_hex("cafe")?, Some(3));
1031 assert_eq!(idx.find_hex("cafe")?, Some(R!(3)));
1019 assert_eq!(idx.find_hex("123A")?, Some(4));
1032 assert_eq!(idx.find_hex("123A")?, Some(R!(4)));
1020 // we masked blocks for all prefixes of "123", including the root
1033 // we masked blocks for all prefixes of "123", including the root
1021 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1034 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1022
1035
1023 eprintln!("{:?}", idx.nt);
1036 eprintln!("{:?}", idx.nt);
1024 idx.insert(5, "c0")?;
1037 idx.insert(5, "c0")?;
1025 assert_eq!(idx.find_hex("cafe")?, Some(3));
1038 assert_eq!(idx.find_hex("cafe")?, Some(R!(3)));
1026 assert_eq!(idx.find_hex("c0")?, Some(5));
1039 assert_eq!(idx.find_hex("c0")?, Some(R!(5)));
1027 assert_eq!(idx.find_hex("c1")?, None);
1040 assert_eq!(idx.find_hex("c1")?, None);
1028 assert_eq!(idx.find_hex("1234")?, Some(0));
1041 assert_eq!(idx.find_hex("1234")?, Some(R!(0)));
1029 // inserting "c0" is just splitting the 'c' slot of the mutable root,
1042 // inserting "c0" is just splitting the 'c' slot of the mutable root,
1030 // it doesn't mask anything
1043 // it doesn't mask anything
1031 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1044 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
@@ -55,7 +55,9 b' pub fn resolve_rev_number_or_hex_prefix('
55 && integer >= 0
55 && integer >= 0
56 && revlog.has_rev(integer.into())
56 && revlog.has_rev(integer.into())
57 {
57 {
58 return Ok(integer);
58 // This is fine because we've just checked that the revision is
59 // valid for the given revlog.
60 return Ok(Revision(integer));
59 }
61 }
60 }
62 }
61 if let Ok(prefix) = NodePrefix::from_hex(input) {
63 if let Ok(prefix) = NodePrefix::from_hex(input) {
@@ -41,22 +41,27 b' pub struct SampleGraph;'
41
41
42 impl Graph for SampleGraph {
42 impl Graph for SampleGraph {
43 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
43 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
44 match rev {
44 let null_rev = NULL_REVISION.0;
45 0 => Ok([NULL_REVISION, NULL_REVISION]),
45 let res = match rev.0 {
46 1 => Ok([0, NULL_REVISION]),
46 0 => Ok([null_rev, null_rev]),
47 2 => Ok([1, NULL_REVISION]),
47 1 => Ok([0, null_rev]),
48 3 => Ok([1, NULL_REVISION]),
48 2 => Ok([1, null_rev]),
49 4 => Ok([2, NULL_REVISION]),
49 3 => Ok([1, null_rev]),
50 5 => Ok([4, NULL_REVISION]),
50 4 => Ok([2, null_rev]),
51 6 => Ok([4, NULL_REVISION]),
51 5 => Ok([4, null_rev]),
52 7 => Ok([4, NULL_REVISION]),
52 6 => Ok([4, null_rev]),
53 8 => Ok([NULL_REVISION, NULL_REVISION]),
53 7 => Ok([4, null_rev]),
54 8 => Ok([null_rev, null_rev]),
54 9 => Ok([6, 7]),
55 9 => Ok([6, 7]),
55 10 => Ok([5, NULL_REVISION]),
56 10 => Ok([5, null_rev]),
56 11 => Ok([3, 7]),
57 11 => Ok([3, 7]),
57 12 => Ok([9, NULL_REVISION]),
58 12 => Ok([9, null_rev]),
58 13 => Ok([8, NULL_REVISION]),
59 13 => Ok([8, null_rev]),
59 r => Err(GraphError::ParentOutOfRange(r)),
60 r => Err(GraphError::ParentOutOfRange(Revision(r))),
61 };
62 match res {
63 Ok([a, b]) => Ok([Revision(a), Revision(b)]),
64 Err(e) => Err(e),
60 }
65 }
61 }
66 }
62 }
67 }
@@ -67,6 +72,6 b' pub type VecGraph = Vec<[Revision; 2]>;'
67
72
68 impl Graph for VecGraph {
73 impl Graph for VecGraph {
69 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
74 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
70 Ok(self[rev as usize])
75 Ok(self[rev.0 as usize])
71 }
76 }
72 }
77 }
@@ -26,25 +26,28 b' fn build_random_graph('
26 if i == 0 || rng.gen_bool(rootprob) {
26 if i == 0 || rng.gen_bool(rootprob) {
27 vg.push([NULL_REVISION, NULL_REVISION])
27 vg.push([NULL_REVISION, NULL_REVISION])
28 } else if i == 1 {
28 } else if i == 1 {
29 vg.push([0, NULL_REVISION])
29 vg.push([Revision(0), NULL_REVISION])
30 } else if rng.gen_bool(mergeprob) {
30 } else if rng.gen_bool(mergeprob) {
31 let p1 = {
31 let p1 = {
32 if i == 2 || rng.gen_bool(prevprob) {
32 if i == 2 || rng.gen_bool(prevprob) {
33 (i - 1) as Revision
33 Revision((i - 1) as BaseRevision)
34 } else {
34 } else {
35 rng.gen_range(0..i - 1) as Revision
35 Revision(rng.gen_range(0..i - 1) as BaseRevision)
36 }
36 }
37 };
37 };
38 // p2 is a random revision lower than i and different from p1
38 // p2 is a random revision lower than i and different from p1
39 let mut p2 = rng.gen_range(0..i - 1) as Revision;
39 let mut p2 = Revision(rng.gen_range(0..i - 1) as BaseRevision);
40 if p2 >= p1 {
40 if p2 >= p1 {
41 p2 += 1;
41 p2.0 += 1;
42 }
42 }
43 vg.push([p1, p2]);
43 vg.push([p1, p2]);
44 } else if rng.gen_bool(prevprob) {
44 } else if rng.gen_bool(prevprob) {
45 vg.push([(i - 1) as Revision, NULL_REVISION])
45 vg.push([Revision((i - 1) as BaseRevision), NULL_REVISION])
46 } else {
46 } else {
47 vg.push([rng.gen_range(0..i - 1) as Revision, NULL_REVISION])
47 vg.push([
48 Revision(rng.gen_range(0..i - 1) as BaseRevision),
49 NULL_REVISION,
50 ])
48 }
51 }
49 }
52 }
50 vg
53 vg
@@ -55,10 +58,10 b' fn ancestors_sets(vg: &VecGraph) -> Vec<'
55 let mut ancs: Vec<HashSet<Revision>> = Vec::new();
58 let mut ancs: Vec<HashSet<Revision>> = Vec::new();
56 (0..vg.len()).for_each(|i| {
59 (0..vg.len()).for_each(|i| {
57 let mut ancs_i = HashSet::new();
60 let mut ancs_i = HashSet::new();
58 ancs_i.insert(i as Revision);
61 ancs_i.insert(Revision(i as BaseRevision));
59 for p in vg[i].iter().cloned() {
62 for p in vg[i].iter().cloned() {
60 if p != NULL_REVISION {
63 if p != NULL_REVISION {
61 ancs_i.extend(&ancs[p as usize]);
64 ancs_i.extend(&ancs[p.0 as usize]);
62 }
65 }
63 }
66 }
64 ancs.push(ancs_i);
67 ancs.push(ancs_i);
@@ -115,7 +118,7 b" impl<'a> NaiveMissingAncestors<'a> {"
115 .push(MissingAncestorsAction::RemoveAncestorsFrom(revs.clone()));
118 .push(MissingAncestorsAction::RemoveAncestorsFrom(revs.clone()));
116 for base in self.bases.iter().cloned() {
119 for base in self.bases.iter().cloned() {
117 if base != NULL_REVISION {
120 if base != NULL_REVISION {
118 for rev in &self.ancestors_sets[base as usize] {
121 for rev in &self.ancestors_sets[base.0 as usize] {
119 revs.remove(rev);
122 revs.remove(rev);
120 }
123 }
121 }
124 }
@@ -131,7 +134,7 b" impl<'a> NaiveMissingAncestors<'a> {"
131 let mut missing: HashSet<Revision> = HashSet::new();
134 let mut missing: HashSet<Revision> = HashSet::new();
132 for rev in revs_as_set.iter().cloned() {
135 for rev in revs_as_set.iter().cloned() {
133 if rev != NULL_REVISION {
136 if rev != NULL_REVISION {
134 missing.extend(&self.ancestors_sets[rev as usize])
137 missing.extend(&self.ancestors_sets[rev.0 as usize])
135 }
138 }
136 }
139 }
137 self.history
140 self.history
@@ -139,7 +142,7 b" impl<'a> NaiveMissingAncestors<'a> {"
139
142
140 for base in self.bases.iter().cloned() {
143 for base in self.bases.iter().cloned() {
141 if base != NULL_REVISION {
144 if base != NULL_REVISION {
142 for rev in &self.ancestors_sets[base as usize] {
145 for rev in &self.ancestors_sets[base.0 as usize] {
143 missing.remove(rev);
146 missing.remove(rev);
144 }
147 }
145 }
148 }
@@ -193,10 +196,10 b' fn sample_revs<R: RngCore>('
193 let sigma = sigma_opt.unwrap_or(0.8);
196 let sigma = sigma_opt.unwrap_or(0.8);
194
197
195 let log_normal = LogNormal::new(mu, sigma).unwrap();
198 let log_normal = LogNormal::new(mu, sigma).unwrap();
196 let nb = min(maxrev as usize, log_normal.sample(rng).floor() as usize);
199 let nb = min(maxrev.0 as usize, log_normal.sample(rng).floor() as usize);
197
200
198 let dist = Uniform::from(NULL_REVISION..maxrev);
201 let dist = Uniform::from(NULL_REVISION.0..maxrev.0);
199 rng.sample_iter(&dist).take(nb).collect()
202 rng.sample_iter(&dist).take(nb).map(Revision).collect()
200 }
203 }
201
204
202 /// Produces the hexadecimal representation of a slice of bytes
205 /// Produces the hexadecimal representation of a slice of bytes
@@ -294,7 +297,7 b' fn test_missing_ancestors_compare_naive('
294 eprintln!("Tested with {} graphs", g);
297 eprintln!("Tested with {} graphs", g);
295 }
298 }
296 let graph = build_random_graph(None, None, None, None);
299 let graph = build_random_graph(None, None, None, None);
297 let graph_len = graph.len() as Revision;
300 let graph_len = Revision(graph.len() as BaseRevision);
298 let ancestors_sets = ancestors_sets(&graph);
301 let ancestors_sets = ancestors_sets(&graph);
299 for _testno in 0..testcount {
302 for _testno in 0..testcount {
300 let bases: HashSet<Revision> =
303 let bases: HashSet<Revision> =
@@ -35,6 +35,7 b''
35 //! [`MissingAncestors`]: struct.MissingAncestors.html
35 //! [`MissingAncestors`]: struct.MissingAncestors.html
36 //! [`AncestorsIterator`]: struct.AncestorsIterator.html
36 //! [`AncestorsIterator`]: struct.AncestorsIterator.html
37 use crate::revlog::pyindex_to_graph;
37 use crate::revlog::pyindex_to_graph;
38 use crate::PyRevision;
38 use crate::{
39 use crate::{
39 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
40 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
40 };
41 };
@@ -54,16 +55,16 b' use vcsgraph::lazy_ancestors::{'
54 py_class!(pub class AncestorsIterator |py| {
55 py_class!(pub class AncestorsIterator |py| {
55 data inner: RefCell<Box<VCGAncestorsIterator<Index>>>;
56 data inner: RefCell<Box<VCGAncestorsIterator<Index>>>;
56
57
57 def __next__(&self) -> PyResult<Option<Revision>> {
58 def __next__(&self) -> PyResult<Option<PyRevision>> {
58 match self.inner(py).borrow_mut().next() {
59 match self.inner(py).borrow_mut().next() {
59 Some(Err(e)) => Err(GraphError::pynew_from_vcsgraph(py, e)),
60 Some(Err(e)) => Err(GraphError::pynew_from_vcsgraph(py, e)),
60 None => Ok(None),
61 None => Ok(None),
61 Some(Ok(r)) => Ok(Some(r)),
62 Some(Ok(r)) => Ok(Some(PyRevision(r))),
62 }
63 }
63 }
64 }
64
65
65 def __contains__(&self, rev: Revision) -> PyResult<bool> {
66 def __contains__(&self, rev: PyRevision) -> PyResult<bool> {
66 self.inner(py).borrow_mut().contains(rev)
67 self.inner(py).borrow_mut().contains(rev.0)
67 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
68 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
68 }
69 }
69
70
@@ -71,13 +72,19 b' py_class!(pub class AncestorsIterator |p'
71 Ok(self.clone_ref(py))
72 Ok(self.clone_ref(py))
72 }
73 }
73
74
74 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
75 def __new__(
75 inclusive: bool) -> PyResult<AncestorsIterator> {
76 _cls,
76 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
77 index: PyObject,
78 initrevs: PyObject,
79 stoprev: PyRevision,
80 inclusive: bool
81 ) -> PyResult<AncestorsIterator> {
82 let index = pyindex_to_graph(py, index)?;
83 let initvec: Vec<_> = rev_pyiter_collect(py, &initrevs, &index)?;
77 let ait = VCGAncestorsIterator::new(
84 let ait = VCGAncestorsIterator::new(
78 pyindex_to_graph(py, index)?,
85 index,
79 initvec,
86 initvec.into_iter().map(|r| r.0),
80 stoprev,
87 stoprev.0,
81 inclusive,
88 inclusive,
82 )
89 )
83 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
90 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
@@ -98,10 +105,10 b' impl AncestorsIterator {'
98 py_class!(pub class LazyAncestors |py| {
105 py_class!(pub class LazyAncestors |py| {
99 data inner: RefCell<Box<VCGLazyAncestors<Index>>>;
106 data inner: RefCell<Box<VCGLazyAncestors<Index>>>;
100
107
101 def __contains__(&self, rev: Revision) -> PyResult<bool> {
108 def __contains__(&self, rev: PyRevision) -> PyResult<bool> {
102 self.inner(py)
109 self.inner(py)
103 .borrow_mut()
110 .borrow_mut()
104 .contains(rev)
111 .contains(rev.0)
105 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
112 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
106 }
113 }
107
114
@@ -113,14 +120,24 b' py_class!(pub class LazyAncestors |py| {'
113 Ok(!self.inner(py).borrow().is_empty())
120 Ok(!self.inner(py).borrow().is_empty())
114 }
121 }
115
122
116 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
123 def __new__(
117 inclusive: bool) -> PyResult<Self> {
124 _cls,
118 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
125 index: PyObject,
126 initrevs: PyObject,
127 stoprev: PyRevision,
128 inclusive: bool
129 ) -> PyResult<Self> {
130 let index = pyindex_to_graph(py, index)?;
131 let initvec: Vec<_> = rev_pyiter_collect(py, &initrevs, &index)?;
119
132
120 let lazy =
133 let lazy =
121 VCGLazyAncestors::new(pyindex_to_graph(py, index)?,
134 VCGLazyAncestors::new(
122 initvec, stoprev, inclusive)
135 index,
123 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
136 initvec.into_iter().map(|r| r.0),
137 stoprev.0,
138 inclusive
139 )
140 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
124
141
125 Self::create_instance(py, RefCell::new(Box::new(lazy)))
142 Self::create_instance(py, RefCell::new(Box::new(lazy)))
126 }
143 }
@@ -129,6 +146,7 b' py_class!(pub class LazyAncestors |py| {'
129
146
130 py_class!(pub class MissingAncestors |py| {
147 py_class!(pub class MissingAncestors |py| {
131 data inner: RefCell<Box<CoreMissing<Index>>>;
148 data inner: RefCell<Box<CoreMissing<Index>>>;
149 data index: RefCell<Index>;
132
150
133 def __new__(
151 def __new__(
134 _cls,
152 _cls,
@@ -136,9 +154,15 b' py_class!(pub class MissingAncestors |py'
136 bases: PyObject
154 bases: PyObject
137 )
155 )
138 -> PyResult<MissingAncestors> {
156 -> PyResult<MissingAncestors> {
139 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
157 let index = pyindex_to_graph(py, index)?;
140 let inner = CoreMissing::new(pyindex_to_graph(py, index)?, bases_vec);
158 let bases_vec: Vec<_> = rev_pyiter_collect(py, &bases, &index)?;
141 MissingAncestors::create_instance(py, RefCell::new(Box::new(inner)))
159
160 let inner = CoreMissing::new(index.clone_ref(py), bases_vec);
161 MissingAncestors::create_instance(
162 py,
163 RefCell::new(Box::new(inner)),
164 RefCell::new(index)
165 )
142 }
166 }
143
167
144 def hasbases(&self) -> PyResult<bool> {
168 def hasbases(&self) -> PyResult<bool> {
@@ -146,8 +170,9 b' py_class!(pub class MissingAncestors |py'
146 }
170 }
147
171
148 def addbases(&self, bases: PyObject) -> PyResult<PyObject> {
172 def addbases(&self, bases: PyObject) -> PyResult<PyObject> {
173 let index = self.index(py).borrow();
174 let bases_vec: Vec<_> = rev_pyiter_collect(py, &bases, &*index)?;
149 let mut inner = self.inner(py).borrow_mut();
175 let mut inner = self.inner(py).borrow_mut();
150 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
151 inner.add_bases(bases_vec);
176 inner.add_bases(bases_vec);
152 // cpython doc has examples with PyResult<()> but this gives me
177 // cpython doc has examples with PyResult<()> but this gives me
153 // the trait `cpython::ToPyObject` is not implemented for `()`
178 // the trait `cpython::ToPyObject` is not implemented for `()`
@@ -155,17 +180,31 b' py_class!(pub class MissingAncestors |py'
155 Ok(py.None())
180 Ok(py.None())
156 }
181 }
157
182
158 def bases(&self) -> PyResult<HashSet<Revision>> {
183 def bases(&self) -> PyResult<HashSet<PyRevision>> {
159 Ok(self.inner(py).borrow().get_bases().clone())
184 Ok(
185 self.inner(py)
186 .borrow()
187 .get_bases()
188 .iter()
189 .map(|r| PyRevision(r.0))
190 .collect()
191 )
160 }
192 }
161
193
162 def basesheads(&self) -> PyResult<HashSet<Revision>> {
194 def basesheads(&self) -> PyResult<HashSet<PyRevision>> {
163 let inner = self.inner(py).borrow();
195 let inner = self.inner(py).borrow();
164 inner.bases_heads().map_err(|e| GraphError::pynew(py, e))
196 Ok(
197 inner
198 .bases_heads()
199 .map_err(|e| GraphError::pynew(py, e))?
200 .into_iter()
201 .map(|r| PyRevision(r.0))
202 .collect()
203 )
165 }
204 }
166
205
167 def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> {
206 def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> {
168 let mut inner = self.inner(py).borrow_mut();
207 let index = self.index(py).borrow();
169 // this is very lame: we convert to a Rust set, update it in place
208 // this is very lame: we convert to a Rust set, update it in place
170 // and then convert back to Python, only to have Python remove the
209 // and then convert back to Python, only to have Python remove the
171 // excess (thankfully, Python is happy with a list or even an iterator)
210 // excess (thankfully, Python is happy with a list or even an iterator)
@@ -174,7 +213,10 b' py_class!(pub class MissingAncestors |py'
174 // discard
213 // discard
175 // - define a trait for sets of revisions in the core and implement
214 // - define a trait for sets of revisions in the core and implement
176 // it for a Python set rewrapped with the GIL marker
215 // it for a Python set rewrapped with the GIL marker
177 let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
216 let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(
217 py, &revs, &*index
218 )?;
219 let mut inner = self.inner(py).borrow_mut();
178 inner.remove_ancestors_from(&mut revs_pyset)
220 inner.remove_ancestors_from(&mut revs_pyset)
179 .map_err(|e| GraphError::pynew(py, e))?;
221 .map_err(|e| GraphError::pynew(py, e))?;
180
222
@@ -182,15 +224,19 b' py_class!(pub class MissingAncestors |py'
182 let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity(
224 let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity(
183 revs_pyset.len());
225 revs_pyset.len());
184 for rev in revs_pyset {
226 for rev in revs_pyset {
185 remaining_pyint_vec.push(rev.to_py_object(py).into_object());
227 remaining_pyint_vec.push(
228 PyRevision(rev.0).to_py_object(py).into_object()
229 );
186 }
230 }
187 let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice());
231 let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice());
188 revs.call_method(py, "intersection_update", (remaining_pylist, ), None)
232 revs.call_method(py, "intersection_update", (remaining_pylist, ), None)
189 }
233 }
190
234
191 def missingancestors(&self, revs: PyObject) -> PyResult<PyList> {
235 def missingancestors(&self, revs: PyObject) -> PyResult<PyList> {
236 let index = self.index(py).borrow();
237 let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs, &*index)?;
238
192 let mut inner = self.inner(py).borrow_mut();
239 let mut inner = self.inner(py).borrow_mut();
193 let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs)?;
194 let missing_vec = match inner.missing_ancestors(revs_vec) {
240 let missing_vec = match inner.missing_ancestors(revs_vec) {
195 Ok(missing) => missing,
241 Ok(missing) => missing,
196 Err(e) => {
242 Err(e) => {
@@ -201,7 +247,9 b' py_class!(pub class MissingAncestors |py'
201 let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity(
247 let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity(
202 missing_vec.len());
248 missing_vec.len());
203 for rev in missing_vec {
249 for rev in missing_vec {
204 missing_pyint_vec.push(rev.to_py_object(py).into_object());
250 missing_pyint_vec.push(
251 PyRevision(rev.0).to_py_object(py).into_object()
252 );
205 }
253 }
206 Ok(PyList::new(py, missing_pyint_vec.as_slice()))
254 Ok(PyList::new(py, missing_pyint_vec.as_slice()))
207 }
255 }
@@ -15,7 +15,7 b' use cpython::{'
15 PyObject, PyResult, PyTuple, Python, PythonObject,
15 PyObject, PyResult, PyTuple, Python, PythonObject,
16 };
16 };
17 use hg::revlog::{Node, RevlogIndex};
17 use hg::revlog::{Node, RevlogIndex};
18 use hg::{Graph, GraphError, Revision};
18 use hg::{BaseRevision, Graph, GraphError, Revision};
19 use libc::{c_int, ssize_t};
19 use libc::{c_int, ssize_t};
20
20
21 const REVLOG_CABI_VERSION: c_int = 3;
21 const REVLOG_CABI_VERSION: c_int = 3;
@@ -145,12 +145,12 b' impl Graph for Index {'
145 let code = unsafe {
145 let code = unsafe {
146 (self.capi.index_parents)(
146 (self.capi.index_parents)(
147 self.index.as_ptr(),
147 self.index.as_ptr(),
148 rev as c_int,
148 rev.0 as c_int,
149 &mut res as *mut [c_int; 2],
149 &mut res as *mut [c_int; 2],
150 )
150 )
151 };
151 };
152 match code {
152 match code {
153 0 => Ok(res),
153 0 => Ok([Revision(res[0]), Revision(res[1])]),
154 _ => Err(GraphError::ParentOutOfRange(rev)),
154 _ => Err(GraphError::ParentOutOfRange(rev)),
155 }
155 }
156 }
156 }
@@ -159,13 +159,17 b' impl Graph for Index {'
159 impl vcsgraph::graph::Graph for Index {
159 impl vcsgraph::graph::Graph for Index {
160 fn parents(
160 fn parents(
161 &self,
161 &self,
162 rev: Revision,
162 rev: BaseRevision,
163 ) -> Result<vcsgraph::graph::Parents, vcsgraph::graph::GraphReadError>
163 ) -> Result<vcsgraph::graph::Parents, vcsgraph::graph::GraphReadError>
164 {
164 {
165 match Graph::parents(self, rev) {
165 // FIXME This trait should be reworked to decide between Revision
166 Ok(parents) => Ok(vcsgraph::graph::Parents(parents)),
166 // and UncheckedRevision, get better errors names, etc.
167 match Graph::parents(self, Revision(rev)) {
168 Ok(parents) => {
169 Ok(vcsgraph::graph::Parents([parents[0].0, parents[1].0]))
170 }
167 Err(GraphError::ParentOutOfRange(rev)) => {
171 Err(GraphError::ParentOutOfRange(rev)) => {
168 Err(vcsgraph::graph::GraphReadError::KeyedInvalidKey(rev))
172 Err(vcsgraph::graph::GraphReadError::KeyedInvalidKey(rev.0))
169 }
173 }
170 }
174 }
171 }
175 }
@@ -174,7 +178,7 b' impl vcsgraph::graph::Graph for Index {'
174 impl vcsgraph::graph::RankedGraph for Index {
178 impl vcsgraph::graph::RankedGraph for Index {
175 fn rank(
179 fn rank(
176 &self,
180 &self,
177 rev: Revision,
181 rev: BaseRevision,
178 ) -> Result<vcsgraph::graph::Rank, vcsgraph::graph::GraphReadError> {
182 ) -> Result<vcsgraph::graph::Rank, vcsgraph::graph::GraphReadError> {
179 match unsafe {
183 match unsafe {
180 (self.capi.fast_rank)(self.index.as_ptr(), rev as ssize_t)
184 (self.capi.fast_rank)(self.index.as_ptr(), rev as ssize_t)
@@ -194,7 +198,7 b' impl RevlogIndex for Index {'
194
198
195 fn node(&self, rev: Revision) -> Option<&Node> {
199 fn node(&self, rev: Revision) -> Option<&Node> {
196 let raw = unsafe {
200 let raw = unsafe {
197 (self.capi.index_node)(self.index.as_ptr(), rev as ssize_t)
201 (self.capi.index_node)(self.index.as_ptr(), rev.0 as ssize_t)
198 };
202 };
199 if raw.is_null() {
203 if raw.is_null() {
200 None
204 None
@@ -8,8 +8,10 b''
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
10
11 use cpython::{ObjectProtocol, PyObject, PyResult, Python};
11 use cpython::{ObjectProtocol, PyErr, PyObject, PyResult, Python};
12 use hg::Revision;
12 use hg::{Revision, RevlogIndex, UncheckedRevision};
13
14 use crate::{exceptions::GraphError, PyRevision};
13
15
14 /// Utility function to convert a Python iterable into various collections
16 /// Utility function to convert a Python iterable into various collections
15 ///
17 ///
@@ -17,11 +19,28 b' use hg::Revision;'
17 /// with `impl IntoIterator<Item=Revision>` arguments, because
19 /// with `impl IntoIterator<Item=Revision>` arguments, because
18 /// a `PyErr` can arise at each step of iteration, whereas these methods
20 /// a `PyErr` can arise at each step of iteration, whereas these methods
19 /// expect iterables over `Revision`, not over some `Result<Revision, PyErr>`
21 /// expect iterables over `Revision`, not over some `Result<Revision, PyErr>`
20 pub fn rev_pyiter_collect<C>(py: Python, revs: &PyObject) -> PyResult<C>
22 pub fn rev_pyiter_collect<C, I>(
23 py: Python,
24 revs: &PyObject,
25 index: &I,
26 ) -> PyResult<C>
21 where
27 where
22 C: FromIterator<Revision>,
28 C: FromIterator<Revision>,
29 I: RevlogIndex,
23 {
30 {
24 revs.iter(py)?
31 revs.iter(py)?
25 .map(|r| r.and_then(|o| o.extract::<Revision>(py)))
32 .map(|r| {
33 r.and_then(|o| match o.extract::<PyRevision>(py) {
34 Ok(r) => index
35 .check_revision(UncheckedRevision(r.0))
36 .ok_or_else(|| {
37 PyErr::new::<GraphError, _>(
38 py,
39 ("InvalidRevision", r.0),
40 )
41 }),
42 Err(e) => Err(e),
43 })
44 })
26 .collect()
45 .collect()
27 }
46 }
@@ -14,6 +14,7 b' use hg::copy_tracing::CombineChangesetCo'
14 use hg::Revision;
14 use hg::Revision;
15
15
16 use crate::pybytes_deref::PyBytesDeref;
16 use crate::pybytes_deref::PyBytesDeref;
17 use crate::PyRevision;
17
18
18 /// Combines copies information contained into revision `revs` to build a copy
19 /// Combines copies information contained into revision `revs` to build a copy
19 /// map.
20 /// map.
@@ -23,14 +24,17 b' pub fn combine_changeset_copies_wrapper('
23 py: Python,
24 py: Python,
24 revs: PyList,
25 revs: PyList,
25 children_count: PyDict,
26 children_count: PyDict,
26 target_rev: Revision,
27 target_rev: PyRevision,
27 rev_info: PyObject,
28 rev_info: PyObject,
28 multi_thread: bool,
29 multi_thread: bool,
29 ) -> PyResult<PyDict> {
30 ) -> PyResult<PyDict> {
31 let target_rev = Revision(target_rev.0);
30 let children_count = children_count
32 let children_count = children_count
31 .items(py)
33 .items(py)
32 .iter()
34 .iter()
33 .map(|(k, v)| Ok((k.extract(py)?, v.extract(py)?)))
35 .map(|(k, v)| {
36 Ok((Revision(k.extract::<PyRevision>(py)?.0), v.extract(py)?))
37 })
34 .collect::<PyResult<_>>()?;
38 .collect::<PyResult<_>>()?;
35
39
36 /// (Revision number, parent 1, parent 2, copy data for this revision)
40 /// (Revision number, parent 1, parent 2, copy data for this revision)
@@ -38,11 +42,13 b' pub fn combine_changeset_copies_wrapper('
38
42
39 let revs_info =
43 let revs_info =
40 revs.iter(py).map(|rev_py| -> PyResult<RevInfo<PyBytes>> {
44 revs.iter(py).map(|rev_py| -> PyResult<RevInfo<PyBytes>> {
41 let rev = rev_py.extract(py)?;
45 let rev = Revision(rev_py.extract::<PyRevision>(py)?.0);
42 let tuple: PyTuple =
46 let tuple: PyTuple =
43 rev_info.call(py, (rev_py,), None)?.cast_into(py)?;
47 rev_info.call(py, (rev_py,), None)?.cast_into(py)?;
44 let p1 = tuple.get_item(py, 0).extract(py)?;
48 let p1 =
45 let p2 = tuple.get_item(py, 1).extract(py)?;
49 Revision(tuple.get_item(py, 0).extract::<PyRevision>(py)?.0);
50 let p2 =
51 Revision(tuple.get_item(py, 1).extract::<PyRevision>(py)?.0);
46 let opt_bytes = tuple.get_item(py, 2).extract(py)?;
52 let opt_bytes = tuple.get_item(py, 2).extract(py)?;
47 Ok((rev, p1, p2, opt_bytes))
53 Ok((rev, p1, p2, opt_bytes))
48 });
54 });
@@ -179,7 +185,7 b' pub fn init_module(py: Python, package: '
179 combine_changeset_copies_wrapper(
185 combine_changeset_copies_wrapper(
180 revs: PyList,
186 revs: PyList,
181 children: PyDict,
187 children: PyDict,
182 target_rev: Revision,
188 target_rev: PyRevision,
183 rev_info: PyObject,
189 rev_info: PyObject,
184 multi_thread: bool
190 multi_thread: bool
185 )
191 )
@@ -9,6 +9,7 b''
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dagop`
11 //! From Python, this will be seen as `mercurial.rustext.dagop`
12 use crate::PyRevision;
12 use crate::{conversion::rev_pyiter_collect, exceptions::GraphError};
13 use crate::{conversion::rev_pyiter_collect, exceptions::GraphError};
13 use cpython::{PyDict, PyModule, PyObject, PyResult, Python};
14 use cpython::{PyDict, PyModule, PyObject, PyResult, Python};
14 use hg::dagops;
15 use hg::dagops;
@@ -26,11 +27,12 b' pub fn headrevs('
26 py: Python,
27 py: Python,
27 index: PyObject,
28 index: PyObject,
28 revs: PyObject,
29 revs: PyObject,
29 ) -> PyResult<HashSet<Revision>> {
30 ) -> PyResult<HashSet<PyRevision>> {
30 let mut as_set: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
31 let index = pyindex_to_graph(py, index)?;
31 dagops::retain_heads(&pyindex_to_graph(py, index)?, &mut as_set)
32 let mut as_set: HashSet<Revision> = rev_pyiter_collect(py, &revs, &index)?;
33 dagops::retain_heads(&index, &mut as_set)
32 .map_err(|e| GraphError::pynew(py, e))?;
34 .map_err(|e| GraphError::pynew(py, e))?;
33 Ok(as_set)
35 Ok(as_set.into_iter().map(Into::into).collect())
34 }
36 }
35
37
36 /// Computes the rank, i.e. the number of ancestors including itself,
38 /// Computes the rank, i.e. the number of ancestors including itself,
@@ -38,10 +40,10 b' pub fn headrevs('
38 pub fn rank(
40 pub fn rank(
39 py: Python,
41 py: Python,
40 index: PyObject,
42 index: PyObject,
41 p1r: Revision,
43 p1r: PyRevision,
42 p2r: Revision,
44 p2r: PyRevision,
43 ) -> PyResult<Rank> {
45 ) -> PyResult<Rank> {
44 node_rank(&pyindex_to_graph(py, index)?, &Parents([p1r, p2r]))
46 node_rank(&pyindex_to_graph(py, index)?, &Parents([p1r.0, p2r.0]))
45 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
47 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
46 }
48 }
47
49
@@ -59,7 +61,7 b' pub fn init_module(py: Python, package: '
59 m.add(
61 m.add(
60 py,
62 py,
61 "rank",
63 "rank",
62 py_fn!(py, rank(index: PyObject, p1r: Revision, p2r: Revision)),
64 py_fn!(py, rank(index: PyObject, p1r: PyRevision, p2r: PyRevision)),
63 )?;
65 )?;
64
66
65 let sys = PyModule::import(py, "sys")?;
67 let sys = PyModule::import(py, "sys")?;
@@ -12,12 +12,13 b''
12 //! - [`PartialDiscover`] is the Rust implementation of
12 //! - [`PartialDiscover`] is the Rust implementation of
13 //! `mercurial.setdiscovery.partialdiscovery`.
13 //! `mercurial.setdiscovery.partialdiscovery`.
14
14
15 use crate::PyRevision;
15 use crate::{
16 use crate::{
16 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
17 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
17 };
18 };
18 use cpython::{
19 use cpython::{
19 ObjectProtocol, PyDict, PyModule, PyObject, PyResult, PyTuple, Python,
20 ObjectProtocol, PyClone, PyDict, PyModule, PyObject, PyResult, PyTuple,
20 PythonObject, ToPyObject,
21 Python, PythonObject, ToPyObject,
21 };
22 };
22 use hg::discovery::PartialDiscovery as CorePartialDiscovery;
23 use hg::discovery::PartialDiscovery as CorePartialDiscovery;
23 use hg::Revision;
24 use hg::Revision;
@@ -29,6 +30,7 b' use crate::revlog::pyindex_to_graph;'
29
30
30 py_class!(pub class PartialDiscovery |py| {
31 py_class!(pub class PartialDiscovery |py| {
31 data inner: RefCell<Box<CorePartialDiscovery<Index>>>;
32 data inner: RefCell<Box<CorePartialDiscovery<Index>>>;
33 data index: RefCell<Index>;
32
34
33 // `_respectsize` is currently only here to replicate the Python API and
35 // `_respectsize` is currently only here to replicate the Python API and
34 // will be used in future patches inside methods that are yet to be
36 // will be used in future patches inside methods that are yet to be
@@ -41,28 +43,33 b' py_class!(pub class PartialDiscovery |py'
41 randomize: bool = true
43 randomize: bool = true
42 ) -> PyResult<PartialDiscovery> {
44 ) -> PyResult<PartialDiscovery> {
43 let index = repo.getattr(py, "changelog")?.getattr(py, "index")?;
45 let index = repo.getattr(py, "changelog")?.getattr(py, "index")?;
46 let index = pyindex_to_graph(py, index)?;
47 let target_heads = rev_pyiter_collect(py, &targetheads, &index)?;
44 Self::create_instance(
48 Self::create_instance(
45 py,
49 py,
46 RefCell::new(Box::new(CorePartialDiscovery::new(
50 RefCell::new(Box::new(CorePartialDiscovery::new(
47 pyindex_to_graph(py, index)?,
51 index.clone_ref(py),
48 rev_pyiter_collect(py, &targetheads)?,
52 target_heads,
49 respectsize,
53 respectsize,
50 randomize,
54 randomize,
51 )))
55 ))),
56 RefCell::new(index),
52 )
57 )
53 }
58 }
54
59
55 def addcommons(&self, commons: PyObject) -> PyResult<PyObject> {
60 def addcommons(&self, commons: PyObject) -> PyResult<PyObject> {
61 let index = self.index(py).borrow();
62 let commons_vec: Vec<_> = rev_pyiter_collect(py, &commons, &*index)?;
56 let mut inner = self.inner(py).borrow_mut();
63 let mut inner = self.inner(py).borrow_mut();
57 let commons_vec: Vec<Revision> = rev_pyiter_collect(py, &commons)?;
58 inner.add_common_revisions(commons_vec)
64 inner.add_common_revisions(commons_vec)
59 .map_err(|e| GraphError::pynew(py, e))?;
65 .map_err(|e| GraphError::pynew(py, e))?;
60 Ok(py.None())
66 Ok(py.None())
61 }
67 }
62
68
63 def addmissings(&self, missings: PyObject) -> PyResult<PyObject> {
69 def addmissings(&self, missings: PyObject) -> PyResult<PyObject> {
70 let index = self.index(py).borrow();
71 let missings_vec: Vec<_> = rev_pyiter_collect(py, &missings, &*index)?;
64 let mut inner = self.inner(py).borrow_mut();
72 let mut inner = self.inner(py).borrow_mut();
65 let missings_vec: Vec<Revision> = rev_pyiter_collect(py, &missings)?;
66 inner.add_missing_revisions(missings_vec)
73 inner.add_missing_revisions(missings_vec)
67 .map_err(|e| GraphError::pynew(py, e))?;
74 .map_err(|e| GraphError::pynew(py, e))?;
68 Ok(py.None())
75 Ok(py.None())
@@ -73,7 +80,10 b' py_class!(pub class PartialDiscovery |py'
73 let mut common: Vec<Revision> = Vec::new();
80 let mut common: Vec<Revision> = Vec::new();
74 for info in sample.iter(py)? { // info is a pair (Revision, bool)
81 for info in sample.iter(py)? { // info is a pair (Revision, bool)
75 let mut revknown = info?.iter(py)?;
82 let mut revknown = info?.iter(py)?;
76 let rev: Revision = revknown.next().unwrap()?.extract(py)?;
83 let rev: PyRevision = revknown.next().unwrap()?.extract(py)?;
84 // This is fine since we're just using revisions as integers
85 // for the purposes of discovery
86 let rev = Revision(rev.0);
77 let known: bool = revknown.next().unwrap()?.extract(py)?;
87 let known: bool = revknown.next().unwrap()?.extract(py)?;
78 if known {
88 if known {
79 common.push(rev);
89 common.push(rev);
@@ -107,9 +117,10 b' py_class!(pub class PartialDiscovery |py'
107 Ok(as_dict)
117 Ok(as_dict)
108 }
118 }
109
119
110 def commonheads(&self) -> PyResult<HashSet<Revision>> {
120 def commonheads(&self) -> PyResult<HashSet<PyRevision>> {
111 self.inner(py).borrow().common_heads()
121 let res = self.inner(py).borrow().common_heads()
112 .map_err(|e| GraphError::pynew(py, e))
122 .map_err(|e| GraphError::pynew(py, e))?;
123 Ok(res.into_iter().map(Into::into).collect())
113 }
124 }
114
125
115 def takefullsample(&self, _headrevs: PyObject,
126 def takefullsample(&self, _headrevs: PyObject,
@@ -119,20 +130,21 b' py_class!(pub class PartialDiscovery |py'
119 .map_err(|e| GraphError::pynew(py, e))?;
130 .map_err(|e| GraphError::pynew(py, e))?;
120 let as_vec: Vec<PyObject> = sample
131 let as_vec: Vec<PyObject> = sample
121 .iter()
132 .iter()
122 .map(|rev| rev.to_py_object(py).into_object())
133 .map(|rev| PyRevision(rev.0).to_py_object(py).into_object())
123 .collect();
134 .collect();
124 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
135 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
125 }
136 }
126
137
127 def takequicksample(&self, headrevs: PyObject,
138 def takequicksample(&self, headrevs: PyObject,
128 size: usize) -> PyResult<PyObject> {
139 size: usize) -> PyResult<PyObject> {
140 let index = self.index(py).borrow();
129 let mut inner = self.inner(py).borrow_mut();
141 let mut inner = self.inner(py).borrow_mut();
130 let revsvec: Vec<Revision> = rev_pyiter_collect(py, &headrevs)?;
142 let revsvec: Vec<_> = rev_pyiter_collect(py, &headrevs, &*index)?;
131 let sample = inner.take_quick_sample(revsvec, size)
143 let sample = inner.take_quick_sample(revsvec, size)
132 .map_err(|e| GraphError::pynew(py, e))?;
144 .map_err(|e| GraphError::pynew(py, e))?;
133 let as_vec: Vec<PyObject> = sample
145 let as_vec: Vec<PyObject> = sample
134 .iter()
146 .iter()
135 .map(|rev| rev.to_py_object(py).into_object())
147 .map(|rev| PyRevision(rev.0).to_py_object(py).into_object())
136 .collect();
148 .collect();
137 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
149 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
138 }
150 }
@@ -18,13 +18,15 b' use cpython::{'
18 };
18 };
19 use hg;
19 use hg;
20
20
21 use crate::PyRevision;
22
21 py_exception!(rustext, GraphError, ValueError);
23 py_exception!(rustext, GraphError, ValueError);
22
24
23 impl GraphError {
25 impl GraphError {
24 pub fn pynew(py: Python, inner: hg::GraphError) -> PyErr {
26 pub fn pynew(py: Python, inner: hg::GraphError) -> PyErr {
25 match inner {
27 match inner {
26 hg::GraphError::ParentOutOfRange(r) => {
28 hg::GraphError::ParentOutOfRange(r) => {
27 GraphError::new(py, ("ParentOutOfRange", r))
29 GraphError::new(py, ("ParentOutOfRange", PyRevision(r.0)))
28 }
30 }
29 }
31 }
30 }
32 }
@@ -24,6 +24,9 b''
24 #![allow(clippy::manual_strip)] // rust-cpython macros
24 #![allow(clippy::manual_strip)] // rust-cpython macros
25 #![allow(clippy::type_complexity)] // rust-cpython macros
25 #![allow(clippy::type_complexity)] // rust-cpython macros
26
26
27 use cpython::{FromPyObject, PyInt, Python, ToPyObject};
28 use hg::{BaseRevision, Revision};
29
27 /// This crate uses nested private macros, `extern crate` is still needed in
30 /// This crate uses nested private macros, `extern crate` is still needed in
28 /// 2018 edition.
31 /// 2018 edition.
29 #[macro_use]
32 #[macro_use]
@@ -44,6 +47,40 b' mod pybytes_deref;'
44 pub mod revlog;
47 pub mod revlog;
45 pub mod utils;
48 pub mod utils;
46
49
50 /// Revision as exposed to/from the Python layer.
51 ///
52 /// We need this indirection because of the orphan rule, meaning we can't
53 /// implement a foreign trait (like [`cpython::ToPyObject`])
54 /// for a foreign type (like [`hg::UncheckedRevision`]).
55 ///
56 /// This also acts as a deterrent against blindly trusting Python to send
57 /// us valid revision numbers.
58 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
59 pub struct PyRevision(BaseRevision);
60
61 impl From<Revision> for PyRevision {
62 fn from(r: Revision) -> Self {
63 PyRevision(r.0)
64 }
65 }
66
67 impl<'s> FromPyObject<'s> for PyRevision {
68 fn extract(
69 py: Python,
70 obj: &'s cpython::PyObject,
71 ) -> cpython::PyResult<Self> {
72 Ok(Self(obj.extract::<BaseRevision>(py)?))
73 }
74 }
75
76 impl ToPyObject for PyRevision {
77 type ObjectType = PyInt;
78
79 fn to_py_object(&self, py: Python) -> Self::ObjectType {
80 self.0.to_py_object(py)
81 }
82 }
83
47 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
84 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
48 m.add(
85 m.add(
49 py,
86 py,
@@ -8,6 +8,7 b''
8 use crate::{
8 use crate::{
9 cindex,
9 cindex,
10 utils::{node_from_py_bytes, node_from_py_object},
10 utils::{node_from_py_bytes, node_from_py_object},
11 PyRevision,
11 };
12 };
12 use cpython::{
13 use cpython::{
13 buffer::{Element, PyBuffer},
14 buffer::{Element, PyBuffer},
@@ -18,7 +19,7 b' use cpython::{'
18 use hg::{
19 use hg::{
19 nodemap::{Block, NodeMapError, NodeTree},
20 nodemap::{Block, NodeMapError, NodeTree},
20 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
21 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
21 Revision, UncheckedRevision,
22 BaseRevision, Revision, UncheckedRevision,
22 };
23 };
23 use std::cell::RefCell;
24 use std::cell::RefCell;
24
25
@@ -59,12 +60,13 b' py_class!(pub class MixedIndex |py| {'
59
60
60 /// Return Revision if found, raises a bare `error.RevlogError`
61 /// Return Revision if found, raises a bare `error.RevlogError`
61 /// in case of ambiguity, same as C version does
62 /// in case of ambiguity, same as C version does
62 def get_rev(&self, node: PyBytes) -> PyResult<Option<Revision>> {
63 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
63 let opt = self.get_nodetree(py)?.borrow();
64 let opt = self.get_nodetree(py)?.borrow();
64 let nt = opt.as_ref().unwrap();
65 let nt = opt.as_ref().unwrap();
65 let idx = &*self.cindex(py).borrow();
66 let idx = &*self.cindex(py).borrow();
66 let node = node_from_py_bytes(py, &node)?;
67 let node = node_from_py_bytes(py, &node)?;
67 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))
68 let res = nt.find_bin(idx, node.into());
69 Ok(res.map_err(|e| nodemap_error(py, e))?.map(Into::into))
68 }
70 }
69
71
70 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
72 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
@@ -72,7 +74,7 b' py_class!(pub class MixedIndex |py| {'
72 ///
74 ///
73 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
75 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
74 /// will catch and rewrap with it
76 /// will catch and rewrap with it
75 def rev(&self, node: PyBytes) -> PyResult<Revision> {
77 def rev(&self, node: PyBytes) -> PyResult<PyRevision> {
76 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
78 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
77 }
79 }
78
80
@@ -131,9 +133,11 b' py_class!(pub class MixedIndex |py| {'
131 let node = node_from_py_object(py, &node_bytes)?;
133 let node = node_from_py_object(py, &node_bytes)?;
132
134
133 let mut idx = self.cindex(py).borrow_mut();
135 let mut idx = self.cindex(py).borrow_mut();
134 let rev = idx.len() as Revision;
135
136
137 // This is ok since we will just add the revision to the index
138 let rev = Revision(idx.len() as BaseRevision);
136 idx.append(py, tup)?;
139 idx.append(py, tup)?;
140
137 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
141 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
138 .insert(&*idx, &node, rev)
142 .insert(&*idx, &node, rev)
139 .map_err(|e| nodemap_error(py, e))?;
143 .map_err(|e| nodemap_error(py, e))?;
@@ -270,7 +274,7 b' py_class!(pub class MixedIndex |py| {'
270 let cindex = self.cindex(py).borrow();
274 let cindex = self.cindex(py).borrow();
271 match item.extract::<i32>(py) {
275 match item.extract::<i32>(py) {
272 Ok(rev) => {
276 Ok(rev) => {
273 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision)
277 Ok(rev >= -1 && rev < cindex.inner().len(py)? as BaseRevision)
274 }
278 }
275 Err(_) => {
279 Err(_) => {
276 cindex.inner().call_method(
280 cindex.inner().call_method(
@@ -331,7 +335,7 b' impl MixedIndex {'
331 ) -> PyResult<PyObject> {
335 ) -> PyResult<PyObject> {
332 let index = self.cindex(py).borrow();
336 let index = self.cindex(py).borrow();
333 for r in 0..index.len() {
337 for r in 0..index.len() {
334 let rev = r as Revision;
338 let rev = Revision(r as BaseRevision);
335 // in this case node() won't ever return None
339 // in this case node() won't ever return None
336 nt.insert(&*index, index.node(rev).unwrap(), rev)
340 nt.insert(&*index, index.node(rev).unwrap(), rev)
337 .map_err(|e| nodemap_error(py, e))?
341 .map_err(|e| nodemap_error(py, e))?
@@ -447,8 +451,10 b' impl MixedIndex {'
447
451
448 let mut nt = NodeTree::load_bytes(Box::new(bytes), len);
452 let mut nt = NodeTree::load_bytes(Box::new(bytes), len);
449
453
450 let data_tip =
454 let data_tip = docket
451 docket.getattr(py, "tip_rev")?.extract::<i32>(py)?.into();
455 .getattr(py, "tip_rev")?
456 .extract::<BaseRevision>(py)?
457 .into();
452 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
458 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
453 let idx = self.cindex(py).borrow();
459 let idx = self.cindex(py).borrow();
454 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
460 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
@@ -456,8 +462,8 b' impl MixedIndex {'
456 })?;
462 })?;
457 let current_tip = idx.len();
463 let current_tip = idx.len();
458
464
459 for r in (data_tip + 1)..current_tip as Revision {
465 for r in (data_tip.0 + 1)..current_tip as BaseRevision {
460 let rev = r as Revision;
466 let rev = Revision(r);
461 // in this case node() won't ever return None
467 // in this case node() won't ever return None
462 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
468 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
463 .map_err(|e| nodemap_error(py, e))?
469 .map_err(|e| nodemap_error(py, e))?
@@ -2,7 +2,6 b' import sys'
2 import unittest
2 import unittest
3
3
4 from mercurial.node import wdirrev
4 from mercurial.node import wdirrev
5 from mercurial import error
6
5
7 from mercurial.testing import revlog as revlogtesting
6 from mercurial.testing import revlog as revlogtesting
8
7
@@ -144,11 +143,15 b' class rustancestorstest(revlogtesting.Re'
144
143
145 def testwdirunsupported(self):
144 def testwdirunsupported(self):
146 # trying to access ancestors of the working directory raises
145 # trying to access ancestors of the working directory raises
147 # WdirUnsupported directly
148 idx = self.parseindex()
146 idx = self.parseindex()
149 with self.assertRaises(error.WdirUnsupported):
147 with self.assertRaises(rustext.GraphError) as arc:
150 list(AncestorsIterator(idx, [wdirrev], -1, False))
148 list(AncestorsIterator(idx, [wdirrev], -1, False))
151
149
150 exc = arc.exception
151 self.assertIsInstance(exc, ValueError)
152 # rust-cpython issues appropriate str instances for Python 2 and 3
153 self.assertEqual(exc.args, ('InvalidRevision', wdirrev))
154
152 def testheadrevs(self):
155 def testheadrevs(self):
153 idx = self.parseindex()
156 idx = self.parseindex()
154 self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3})
157 self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3})
General Comments 0
You need to be logged in to leave comments. Login now