comparison rust/hg-core/src/utils/files.rs @ 42586:cad3dde7a573

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.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 30 Jun 2019 18:32:43 +0900
parents f305f1d7d559
children 4b3b27d567d5
comparison
equal deleted inserted replaced
42585:ebf353aa4385 42586:cad3dde7a573
1 use std::iter::FusedIterator;
1 use std::path::Path; 2 use std::path::Path;
2 3
3 pub fn get_path_from_bytes(bytes: &[u8]) -> &Path { 4 pub fn get_path_from_bytes(bytes: &[u8]) -> &Path {
4 let os_str; 5 let os_str;
5 #[cfg(unix)] 6 #[cfg(unix)]
15 os_str = std::ffi::OsString::from_wide(bytes); 16 os_str = std::ffi::OsString::from_wide(bytes);
16 } 17 }
17 18
18 Path::new(os_str) 19 Path::new(os_str)
19 } 20 }
21
22 /// An iterator over repository path yielding itself and its ancestors.
23 #[derive(Copy, Clone, Debug)]
24 pub struct Ancestors<'a> {
25 next: Option<&'a [u8]>,
26 }
27
28 impl<'a> Iterator for Ancestors<'a> {
29 // if we had an HgPath type, this would yield &'a HgPath
30 type Item = &'a [u8];
31
32 fn next(&mut self) -> Option<Self::Item> {
33 let next = self.next;
34 self.next = match self.next {
35 Some(s) if s.is_empty() => None,
36 Some(s) => {
37 let p = s.iter().rposition(|&c| c == b'/').unwrap_or(0);
38 Some(&s[..p])
39 }
40 None => None,
41 };
42 next
43 }
44 }
45
46 impl<'a> FusedIterator for Ancestors<'a> {}
47
48 /// Returns an iterator yielding ancestor directories of the given repository
49 /// path.
50 ///
51 /// The path is separated by '/', and must not start with '/'.
52 ///
53 /// The path itself isn't included unless it is b"" (meaning the root
54 /// directory.)
55 pub fn find_dirs<'a>(path: &'a [u8]) -> Ancestors<'a> {
56 let mut dirs = Ancestors { next: Some(path) };
57 if !path.is_empty() {
58 dirs.next(); // skip itself
59 }
60 dirs
61 }
62
63 #[cfg(test)]
64 mod tests {
65 #[test]
66 fn find_dirs_some() {
67 let mut dirs = super::find_dirs(b"foo/bar/baz");
68 assert_eq!(dirs.next(), Some(b"foo/bar".as_ref()));
69 assert_eq!(dirs.next(), Some(b"foo".as_ref()));
70 assert_eq!(dirs.next(), Some(b"".as_ref()));
71 assert_eq!(dirs.next(), None);
72 assert_eq!(dirs.next(), None);
73 }
74
75 #[test]
76 fn find_dirs_empty() {
77 // looks weird, but mercurial.util.finddirs(b"") yields b""
78 let mut dirs = super::find_dirs(b"");
79 assert_eq!(dirs.next(), Some(b"".as_ref()));
80 assert_eq!(dirs.next(), None);
81 assert_eq!(dirs.next(), None);
82 }
83 }