From 2513120ff5826bfa9c25b5c47810af68fdafcadf Mon Sep 17 00:00:00 2001 From: edef Date: Wed, 1 May 2024 13:05:27 +0000 Subject: feat(tvix/castore/path): add PathBuf::{new, with_capacity, push} Change-Id: Ia64f4bda80e91adbdb804f4f26cef5ace8f5406a Reviewed-on: https://cl.tvl.fyi/c/depot/+/11571 Tested-by: BuildkiteCI Reviewed-by: flokli --- tvix/castore/src/path.rs | 49 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) (limited to 'tvix/castore/src') diff --git a/tvix/castore/src/path.rs b/tvix/castore/src/path.rs index 7e0ad391c7ca..34fac5a2f8c0 100644 --- a/tvix/castore/src/path.rs +++ b/tvix/castore/src/path.rs @@ -72,18 +72,13 @@ impl Path { ) } + /// Creates a PathBuf with `name` adjoined to self. pub fn join(&self, name: &[u8]) -> Result { - if name.contains(&b'/') || name.is_empty() { - return Err(std::io::ErrorKind::InvalidData.into()); - } - - let mut v = self.inner.to_vec(); - if !v.is_empty() { - v.extend_from_slice(b"/"); - } - v.extend_from_slice(name); + let mut v = PathBuf::with_capacity(self.inner.len() + name.len() + 1); + v.inner.extend_from_slice(&self.inner); + v.push(name)?; - Ok(PathBuf { inner: v }) + Ok(v) } /// Produces an iterator over the components of the path, which are @@ -197,6 +192,30 @@ impl Display for PathBuf { } impl PathBuf { + pub fn new() -> PathBuf { + Self::default() + } + + pub fn with_capacity(capacity: usize) -> PathBuf { + // SAFETY: The empty path is a valid path. + Self { + inner: Vec::with_capacity(capacity), + } + } + + /// Adjoins `name` to self. + pub fn push(&mut self, name: &[u8]) -> Result<(), std::io::Error> { + validate_node_name(name).map_err(|_| std::io::ErrorKind::InvalidData)?; + + if !self.inner.is_empty() { + self.inner.push(b'/'); + } + + self.inner.extend_from_slice(name); + + Ok(()) + } + /// Convert a byte vector to a PathBuf, without checking validity. unsafe fn from_bytes_unchecked(bytes: Vec) -> PathBuf { PathBuf { inner: bytes } @@ -279,8 +298,10 @@ mod test { #[rstest] #[case("a", "b", "a/b")] #[case("a", "b", "a/b")] - pub fn join(#[case] p: PathBuf, #[case] name: &str, #[case] exp_p: PathBuf) { + pub fn join_push(#[case] mut p: PathBuf, #[case] name: &str, #[case] exp_p: PathBuf) { assert_eq!(exp_p, p.join(name.as_bytes()).expect("join failed")); + p.push(name.as_bytes()).expect("push failed"); + assert_eq!(exp_p, p); } #[rstest] @@ -290,9 +311,13 @@ mod test { #[case("", "/")] #[case("", "")] #[case("", "b/c")] - pub fn join_fail(#[case] p: PathBuf, #[case] name: &str) { + #[case("", ".")] + #[case("", "..")] + pub fn join_push_fail(#[case] mut p: PathBuf, #[case] name: &str) { p.join(name.as_bytes()) .expect_err("join succeeded unexpectedly"); + p.push(name.as_bytes()) + .expect_err("push succeeded unexpectedly"); } #[rstest] -- cgit 1.4.1