diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 7812485ef..bba8bae8a 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -1262,7 +1262,7 @@ pub fn optimize(expression: Rc) -> Rc { fold_difference(&expression).unwrap_or(expression) } -pub trait Revset<'repo> { +pub trait Revset<'repo>: ToPredicateFn<'repo> { // All revsets currently iterate in order of descending index position fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo>; @@ -1271,6 +1271,23 @@ pub trait Revset<'repo> { } } +// This trait is implementation detail, which can be hidden in private module. +pub trait ToPredicateFn<'repo> { + /// Creates function that tests if the given entry is included in the set. + /// + /// The predicate function is evaluated in order of `RevsetIterator`. + fn to_predicate_fn(&self) -> Box) -> bool + '_>; +} + +impl<'repo, T> ToPredicateFn<'repo> for Box +where + T: ToPredicateFn<'repo> + ?Sized, +{ + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + >::to_predicate_fn(self) + } +} + pub struct RevsetIterator<'revset, 'repo: 'revset> { inner: Box> + 'revset>, } @@ -1300,6 +1317,16 @@ impl<'revset, 'repo> RevsetIterator<'revset, 'repo> { pub fn graph(self) -> RevsetGraphIterator<'revset, 'repo> { RevsetGraphIterator::new(self) } + + fn into_predicate_fn(self) -> Box) -> bool + 'revset> { + let mut iter = self.fuse().peekable(); + Box::new(move |entry| { + while iter.next_if(|e| e.position() > entry.position()).is_some() { + continue; + } + iter.next_if(|e| e.position() == entry.position()).is_some() + }) + } } impl<'repo> Iterator for RevsetIterator<'_, 'repo> { @@ -1367,6 +1394,12 @@ impl<'repo> Revset<'repo> for EagerRevset<'repo> { } } +impl<'repo> ToPredicateFn<'repo> for EagerRevset<'repo> { + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + self.iter().into_predicate_fn() + } +} + struct RevWalkRevset<'repo> { walk: RevWalk<'repo>, } @@ -1377,6 +1410,12 @@ impl<'repo> Revset<'repo> for RevWalkRevset<'repo> { } } +impl<'repo> ToPredicateFn<'repo> for RevWalkRevset<'repo> { + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + self.iter().into_predicate_fn() + } +} + struct ChildrenRevset<'revset, 'repo: 'revset> { // The revisions we want to find children for root_set: Box + 'revset>, @@ -1403,14 +1442,37 @@ impl<'repo> Revset<'repo> for ChildrenRevset<'_, 'repo> { } } -struct FilterRevset<'revset, 'repo: 'revset> { - candidates: Box + 'revset>, - predicate: Box) -> bool + 'repo>, +impl<'repo> ToPredicateFn<'repo> for ChildrenRevset<'_, 'repo> { + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + // TODO: can be optimized if candidate_set contains all heads + self.iter().into_predicate_fn() + } } -impl<'repo> Revset<'repo> for FilterRevset<'_, 'repo> { +struct FilterRevset<'revset, 'repo: 'revset, P> { + candidates: Box + 'revset>, + predicate: P, +} + +impl<'repo, P> Revset<'repo> for FilterRevset<'_, 'repo, P> +where + P: ToPredicateFn<'repo>, +{ fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> { - RevsetIterator::new(Box::new(self.candidates.iter().filter(&self.predicate))) + let p = self.predicate.to_predicate_fn(); + RevsetIterator::new(Box::new(self.candidates.iter().filter(p))) + } +} + +impl<'repo, P> ToPredicateFn<'repo> for FilterRevset<'_, 'repo, P> +where + P: ToPredicateFn<'repo>, +{ + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + // TODO: optimize 'p1' out if candidates = All + let mut p1 = self.candidates.to_predicate_fn(); + let mut p2 = self.predicate.to_predicate_fn(); + Box::new(move |entry| p1(entry) && p2(entry)) } } @@ -1428,6 +1490,14 @@ impl<'repo> Revset<'repo> for UnionRevset<'_, 'repo> { } } +impl<'repo> ToPredicateFn<'repo> for UnionRevset<'_, 'repo> { + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + let mut p1 = self.set1.to_predicate_fn(); + let mut p2 = self.set2.to_predicate_fn(); + Box::new(move |entry| p1(entry) || p2(entry)) + } +} + struct UnionRevsetIterator<'revset, 'repo> { iter1: Peekable>, iter2: Peekable>, @@ -1466,6 +1536,14 @@ impl<'repo> Revset<'repo> for IntersectionRevset<'_, 'repo> { } } +impl<'repo> ToPredicateFn<'repo> for IntersectionRevset<'_, 'repo> { + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + let mut p1 = self.set1.to_predicate_fn(); + let mut p2 = self.set2.to_predicate_fn(); + Box::new(move |entry| p1(entry) && p2(entry)) + } +} + struct IntersectionRevsetIterator<'revset, 'repo> { iter1: Peekable>, iter2: Peekable>, @@ -1516,6 +1594,15 @@ impl<'repo> Revset<'repo> for DifferenceRevset<'_, 'repo> { } } +impl<'repo> ToPredicateFn<'repo> for DifferenceRevset<'_, 'repo> { + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + // TODO: optimize 'p1' out for unary negate? + let mut p1 = self.set1.to_predicate_fn(); + let mut p2 = self.set2.to_predicate_fn(); + Box::new(move |entry| p1(entry) && !p2(entry)) + } +} + struct DifferenceRevsetIterator<'revset, 'repo> { iter1: Peekable>, iter2: Peekable>, @@ -1779,10 +1866,18 @@ pub fn revset_for_commits<'revset, 'repo: 'revset>( Box::new(EagerRevset { index_entries }) } +type PurePredicateFn<'repo> = Box) -> bool + 'repo>; + +impl<'repo> ToPredicateFn<'repo> for PurePredicateFn<'repo> { + fn to_predicate_fn(&self) -> Box) -> bool + '_> { + Box::new(self) + } +} + fn build_predicate_fn<'repo>( repo: RepoRef<'repo>, predicate: &RevsetFilterPredicate, -) -> Box) -> bool + 'repo> { +) -> PurePredicateFn<'repo> { match predicate { RevsetFilterPredicate::ParentCount(parent_count_range) => { let parent_count_range = parent_count_range.clone(); @@ -1833,7 +1928,7 @@ pub fn filter_by_diff<'revset, 'repo: 'revset>( matcher: impl Borrow + 'repo, candidates: Box + 'revset>, ) -> Box + 'revset> { - Box::new(FilterRevset { + Box::new(FilterRevset:: { candidates, predicate: Box::new(move |entry| has_diff_from_parent(repo, entry, matcher.borrow())), }) @@ -2885,16 +2980,48 @@ mod tests { let get_entry = |id: &CommitId| index.entry_by_id(id).unwrap(); let make_entries = |ids: &[&CommitId]| ids.iter().map(|id| get_entry(id)).collect_vec(); - let make_set = |ids: &[&CommitId]| -> Box> { + let make_set = |ids: &[&CommitId]| -> Box + '_> { let index_entries = make_entries(ids); Box::new(EagerRevset { index_entries }) }; - let set = FilterRevset { + let set = make_set(&[&id_4, &id_3, &id_2, &id_0]); + let mut p = set.to_predicate_fn(); + assert!(p(&get_entry(&id_4))); + assert!(p(&get_entry(&id_3))); + assert!(p(&get_entry(&id_2))); + assert!(!p(&get_entry(&id_1))); + assert!(p(&get_entry(&id_0))); + // Uninteresting entries can be skipped + let mut p = set.to_predicate_fn(); + assert!(p(&get_entry(&id_3))); + assert!(!p(&get_entry(&id_1))); + assert!(p(&get_entry(&id_0))); + + let set = FilterRevset:: { candidates: make_set(&[&id_4, &id_2, &id_0]), predicate: Box::new(|entry| entry.commit_id() != id_4), }; assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2, &id_0])); + let mut p = set.to_predicate_fn(); + assert!(!p(&get_entry(&id_4))); + assert!(!p(&get_entry(&id_3))); + assert!(p(&get_entry(&id_2))); + assert!(!p(&get_entry(&id_1))); + assert!(p(&get_entry(&id_0))); + + // Intersection by FilterRevset + let set = FilterRevset { + candidates: make_set(&[&id_4, &id_2, &id_0]), + predicate: make_set(&[&id_3, &id_2, &id_1]), + }; + assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2])); + let mut p = set.to_predicate_fn(); + assert!(!p(&get_entry(&id_4))); + assert!(!p(&get_entry(&id_3))); + assert!(p(&get_entry(&id_2))); + assert!(!p(&get_entry(&id_1))); + assert!(!p(&get_entry(&id_0))); let set = UnionRevset { set1: make_set(&[&id_4, &id_2]), @@ -2904,17 +3031,35 @@ mod tests { set.iter().collect_vec(), make_entries(&[&id_4, &id_3, &id_2, &id_1]) ); + let mut p = set.to_predicate_fn(); + assert!(p(&get_entry(&id_4))); + assert!(p(&get_entry(&id_3))); + assert!(p(&get_entry(&id_2))); + assert!(p(&get_entry(&id_1))); + assert!(!p(&get_entry(&id_0))); let set = IntersectionRevset { set1: make_set(&[&id_4, &id_2, &id_0]), set2: make_set(&[&id_3, &id_2, &id_1]), }; assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2])); + let mut p = set.to_predicate_fn(); + assert!(!p(&get_entry(&id_4))); + assert!(!p(&get_entry(&id_3))); + assert!(p(&get_entry(&id_2))); + assert!(!p(&get_entry(&id_1))); + assert!(!p(&get_entry(&id_0))); let set = DifferenceRevset { set1: make_set(&[&id_4, &id_2, &id_0]), set2: make_set(&[&id_3, &id_2, &id_1]), }; assert_eq!(set.iter().collect_vec(), make_entries(&[&id_4, &id_0])); + let mut p = set.to_predicate_fn(); + assert!(p(&get_entry(&id_4))); + assert!(!p(&get_entry(&id_3))); + assert!(!p(&get_entry(&id_2))); + assert!(!p(&get_entry(&id_1))); + assert!(p(&get_entry(&id_0))); } }