[PATCH 1 of 2] rust-dirstate: add helper to iterate ancestor paths

Yuya Nishihara yuya at tcha.org
Sun Jun 30 11:05:34 UTC 2019


# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1561887163 -32400
#      Sun Jun 30 18:32:43 2019 +0900
# Node ID 72ab74c704053b2212b874a902574fedadad4252
# Parent  904e0da2e195d2a29977ceccdd25480233c34d7a
rust-dirstate: add helper to iterate ancestor paths

This is modeled after std::path::Path::ancestors().

find_dirs(b"") yields b"" because Mercurial's util.finddirs() works in that
way, and the test case for DirsMultiset expects such behavior.

diff --git a/rust/hg-core/src/utils/files.rs b/rust/hg-core/src/utils/files.rs
--- a/rust/hg-core/src/utils/files.rs
+++ b/rust/hg-core/src/utils/files.rs
@@ -1,3 +1,4 @@
+use std::iter::FusedIterator;
 use std::path::Path;
 
 pub fn get_path_from_bytes(bytes: &[u8]) -> &Path {
@@ -17,3 +18,66 @@ pub fn get_path_from_bytes(bytes: &[u8])
 
     Path::new(os_str)
 }
+
+/// An iterator over repository path yielding itself and its ancestors.
+#[derive(Copy, Clone, Debug)]
+pub struct Ancestors<'a> {
+    next: Option<&'a [u8]>,
+}
+
+impl<'a> Iterator for Ancestors<'a> {
+    // if we had an HgPath type, this would yield &'a HgPath
+    type Item = &'a [u8];
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let next = self.next;
+        self.next = match self.next {
+            Some(s) if s.is_empty() => None,
+            Some(s) => {
+                let p = s.iter().rposition(|&c| c == b'/').unwrap_or(0);
+                Some(&s[..p])
+            }
+            None => None,
+        };
+        next
+    }
+}
+
+impl<'a> FusedIterator for Ancestors<'a> {}
+
+/// Returns an iterator yielding ancestor directories of the given repository
+/// path.
+///
+/// The path is separated by '/', and must not start with '/'.
+///
+/// The path itself isn't included unless it is b"" (meaning the root
+/// directory.)
+pub fn find_dirs<'a>(path: &'a [u8]) -> Ancestors<'a> {
+    let mut dirs = Ancestors { next: Some(path) };
+    if !path.is_empty() {
+        dirs.next(); // skip itself
+    }
+    dirs
+}
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn find_dirs_some() {
+        let mut dirs = super::find_dirs(b"foo/bar/baz");
+        assert_eq!(dirs.next(), Some(b"foo/bar".as_ref()));
+        assert_eq!(dirs.next(), Some(b"foo".as_ref()));
+        assert_eq!(dirs.next(), Some(b"".as_ref()));
+        assert_eq!(dirs.next(), None);
+        assert_eq!(dirs.next(), None);
+    }
+
+    #[test]
+    fn find_dirs_empty() {
+        // looks weird, but mercurial.util.finddirs(b"") yields b""
+        let mut dirs = super::find_dirs(b"");
+        assert_eq!(dirs.next(), Some(b"".as_ref()));
+        assert_eq!(dirs.next(), None);
+        assert_eq!(dirs.next(), None);
+    }
+}


More information about the Mercurial-devel mailing list