From cfa067a0a997269342259cf6266e7225802488d2 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 10 Mar 2024 00:03:33 +0900 Subject: [PATCH] index: add peekable RevWalk adapter This helps to migrate union/intersection/difference iterators to RevWalk. --- lib/src/default_index/rev_walk.rs | 79 ++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/lib/src/default_index/rev_walk.rs b/lib/src/default_index/rev_walk.rs index 36eb0ec0f..35181190c 100644 --- a/lib/src/default_index/rev_walk.rs +++ b/lib/src/default_index/rev_walk.rs @@ -39,6 +39,17 @@ pub(super) trait RevWalk { // The following methods are provided for convenience. They are not supposed // to be reimplemented. + /// Wraps in adapter that can peek one more item without consuming. + fn peekable(self) -> PeekableRevWalk + where + Self: Sized, + { + PeekableRevWalk { + walk: self, + peeked: None, + } + } + /// Reattaches the underlying `index`. fn attach(self, index: &I) -> RevWalkBorrowedIndexIter<'_, I, Self> where @@ -57,7 +68,7 @@ pub(super) struct EagerRevWalk { } impl EagerRevWalk { - #[allow(unused)] // TODO + #[cfg(test)] // TODO pub fn new(iter: T) -> Self { EagerRevWalk { iter: iter.fuse() } } @@ -71,6 +82,48 @@ impl RevWalk for EagerRevWalk { } } +#[derive(Clone, Debug)] +#[must_use] +pub(super) struct PeekableRevWalk> { + walk: W, + // Since RevWalk is fused, we don't need a nested Option>. + peeked: Option, +} + +impl> PeekableRevWalk { + #[cfg(test)] // TODO + pub fn peek(&mut self, index: &I) -> Option<&W::Item> { + if self.peeked.is_none() { + self.peeked = self.walk.next(index); + } + self.peeked.as_ref() + } + + #[cfg(test)] // TODO + pub fn next_if( + &mut self, + index: &I, + predicate: impl FnOnce(&W::Item) -> bool, + ) -> Option { + match self.next(index) { + Some(item) if predicate(&item) => Some(item), + other => { + assert!(self.peeked.is_none()); + self.peeked = other; + None + } + } + } +} + +impl> RevWalk for PeekableRevWalk { + type Item = W::Item; + + fn next(&mut self, index: &I) -> Option { + self.peeked.take().or_else(|| self.walk.next(index)) + } +} + /// Adapter that turns `RevWalk` into `Iterator` by attaching borrowed `index`. #[derive(Clone, Debug)] #[must_use] @@ -701,6 +754,30 @@ mod tests { .collect() } + #[test] + fn test_peekable_rev_walk() { + let source = EagerRevWalk::new(vec![0, 1, 2, 3].into_iter()); + let mut peekable = source.peekable(); + assert_eq!(peekable.peek(&()), Some(&0)); + assert_eq!(peekable.peek(&()), Some(&0)); + assert_eq!(peekable.next(&()), Some(0)); + assert_eq!(peekable.peeked, None); + assert_eq!(peekable.next_if(&(), |&v| v == 2), None); + assert_eq!(peekable.next(&()), Some(1)); + assert_eq!(peekable.next_if(&(), |&v| v == 2), Some(2)); + assert_eq!(peekable.peeked, None); + assert_eq!(peekable.peek(&()), Some(&3)); + assert_eq!(peekable.next_if(&(), |&v| v == 3), Some(3)); + assert_eq!(peekable.peeked, None); + assert_eq!(peekable.next(&()), None); + assert_eq!(peekable.next(&()), None); + + let source = EagerRevWalk::new((vec![] as Vec).into_iter()); + let mut peekable = source.peekable(); + assert_eq!(peekable.peek(&()), None); + assert_eq!(peekable.next(&()), None); + } + #[test] fn test_walk_ancestors() { let mut new_change_id = change_id_generator();