changeset 43833:4f1543a2f5c3

rust-hg-path: add method to get part of a path relative to a prefix This will be used in the next patch in this series. Differential Revision: https://phab.mercurial-scm.org/D7526
author Raphaël Gomès <rgomes@octobus.net>
date Fri, 29 Nov 2019 17:24:40 +0100
parents 1bb4e9b02984
children 542c8b277261
files rust/hg-core/src/utils/hg_path.rs
diffstat 1 files changed, 49 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-core/src/utils/hg_path.rs	Fri Nov 29 18:33:56 2019 +0100
+++ b/rust/hg-core/src/utils/hg_path.rs	Fri Nov 29 17:24:40 2019 +0100
@@ -112,6 +112,9 @@
     pub fn contains(&self, other: u8) -> bool {
         self.inner.contains(&other)
     }
+    pub fn starts_with(&self, needle: impl AsRef<HgPath>) -> bool {
+        self.inner.starts_with(needle.as_ref().as_bytes())
+    }
     pub fn join<T: ?Sized + AsRef<HgPath>>(&self, other: &T) -> HgPathBuf {
         let mut inner = self.inner.to_owned();
         if inner.len() != 0 && inner.last() != Some(&b'/') {
@@ -120,6 +123,21 @@
         inner.extend(other.as_ref().bytes());
         HgPathBuf::from_bytes(&inner)
     }
+    /// Given a base directory, returns the slice of `self` relative to the
+    /// base directory. If `base` is not a directory (does not end with a
+    /// `b'/'`), returns `None`.
+    pub fn relative_to(&self, base: impl AsRef<HgPath>) -> Option<&HgPath> {
+        let base = base.as_ref();
+        if base.is_empty() {
+            return Some(self);
+        }
+        let is_dir = base.as_bytes().ends_with(b"/");
+        if is_dir && self.starts_with(base) {
+            Some(HgPath::new(&self.inner[base.len()..]))
+        } else {
+            None
+        }
+    }
     /// Checks for errors in the path, short-circuiting at the first one.
     /// This generates fine-grained errors useful for debugging.
     /// To simply check if the path is valid during tests, use `is_valid`.
@@ -412,4 +430,35 @@
         let path = HgPathBuf::from_bytes(b"a").join(HgPath::new(b"/b"));
         assert_eq!(b"a//b", path.as_bytes());
     }
+
+    #[test]
+    fn test_relative_to() {
+        let path = HgPath::new(b"");
+        let base = HgPath::new(b"");
+        assert_eq!(Some(path), path.relative_to(base));
+
+        let path = HgPath::new(b"path");
+        let base = HgPath::new(b"");
+        assert_eq!(Some(path), path.relative_to(base));
+
+        let path = HgPath::new(b"a");
+        let base = HgPath::new(b"b");
+        assert_eq!(None, path.relative_to(base));
+
+        let path = HgPath::new(b"a/b");
+        let base = HgPath::new(b"a");
+        assert_eq!(None, path.relative_to(base));
+
+        let path = HgPath::new(b"a/b");
+        let base = HgPath::new(b"a/");
+        assert_eq!(Some(HgPath::new(b"b")), path.relative_to(base));
+
+        let path = HgPath::new(b"nested/path/to/b");
+        let base = HgPath::new(b"nested/path/");
+        assert_eq!(Some(HgPath::new(b"to/b")), path.relative_to(base));
+
+        let path = HgPath::new(b"ends/with/dir/");
+        let base = HgPath::new(b"ends/");
+        assert_eq!(Some(HgPath::new(b"with/dir/")), path.relative_to(base));
+    }
 }