about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/nix-compat/src/store_path/mod.rs94
1 files changed, 72 insertions, 22 deletions
diff --git a/tvix/nix-compat/src/store_path/mod.rs b/tvix/nix-compat/src/store_path/mod.rs
index 7c0ff7eab173..e2c1de8a95c6 100644
--- a/tvix/nix-compat/src/store_path/mod.rs
+++ b/tvix/nix-compat/src/store_path/mod.rs
@@ -96,28 +96,7 @@ impl StorePath {
     /// Construct a [StorePath] by passing the `$digest-$name` string
     /// that comes after [STORE_DIR_WITH_SLASH].
     pub fn from_bytes(s: &[u8]) -> Result<StorePath, Error> {
-        // the whole string needs to be at least:
-        //
-        // - 32 characters (encoded hash)
-        // - 1 dash
-        // - 1 character for the name
-        if s.len() < ENCODED_DIGEST_SIZE + 2 {
-            Err(Error::InvalidLength)?
-        }
-
-        let digest = match nixbase32::decode(&s[..ENCODED_DIGEST_SIZE]) {
-            Ok(decoded) => decoded,
-            Err(decoder_error) => return Err(Error::InvalidHashEncoding(decoder_error)),
-        };
-
-        if s[ENCODED_DIGEST_SIZE] != b'-' {
-            return Err(Error::MissingDash);
-        }
-
-        Ok(StorePath {
-            name: validate_name(&s[ENCODED_DIGEST_SIZE + 1..])?.to_owned(),
-            digest: digest.try_into().expect("size is known"),
-        })
+        Ok(StorePathRef::from_bytes(s)?.into())
     }
 
     /// Construct a [StorePath] from an absolute store path string.
@@ -175,6 +154,71 @@ impl StorePath {
     }
 }
 
+/// Like [StorePath], but without a heap allocation for the name.
+/// Used by [StorePath] for parsing.
+///
+/// TODO(edef): migrate most methods here
+#[derive(Debug)]
+pub struct StorePathRef<'a> {
+    digest: [u8; DIGEST_SIZE],
+    name: &'a str,
+}
+
+impl From<StorePathRef<'_>> for StorePath {
+    fn from(StorePathRef { digest, name }: StorePathRef<'_>) -> Self {
+        StorePath {
+            digest,
+            name: name.to_owned(),
+        }
+    }
+}
+
+impl<'a> From<&'a StorePath> for StorePathRef<'a> {
+    fn from(&StorePath { digest, ref name }: &'a StorePath) -> Self {
+        StorePathRef {
+            digest,
+            name: name.as_ref(),
+        }
+    }
+}
+
+impl<'a> StorePathRef<'a> {
+    pub fn digest(&self) -> &[u8; DIGEST_SIZE] {
+        &self.digest
+    }
+
+    pub fn name(&self) -> &'a str {
+        self.name
+    }
+
+    /// Construct a [StorePathRef] by passing the `$digest-$name` string
+    /// that comes after [STORE_DIR_WITH_SLASH].
+    pub fn from_bytes(s: &'a [u8]) -> Result<Self, Error> {
+        // the whole string needs to be at least:
+        //
+        // - 32 characters (encoded hash)
+        // - 1 dash
+        // - 1 character for the name
+        if s.len() < ENCODED_DIGEST_SIZE + 2 {
+            Err(Error::InvalidLength)?
+        }
+
+        let digest = match nixbase32::decode(&s[..ENCODED_DIGEST_SIZE]) {
+            Ok(decoded) => decoded,
+            Err(decoder_error) => return Err(Error::InvalidHashEncoding(decoder_error)),
+        };
+
+        if s[ENCODED_DIGEST_SIZE] != b'-' {
+            return Err(Error::MissingDash);
+        }
+
+        Ok(StorePathRef {
+            digest: digest.try_into().expect("size is known"),
+            name: validate_name(&s[ENCODED_DIGEST_SIZE + 1..])?,
+        })
+    }
+}
+
 /// NAME_CHARS contains `true` for bytes that are valid in store path names,
 /// not accounting for '.' being permitted only past the first character.
 static NAME_CHARS: [bool; 256] = {
@@ -231,6 +275,12 @@ pub(crate) fn validate_name(s: &(impl AsRef<[u8]> + ?Sized)) -> Result<&str, Err
 }
 
 impl fmt::Display for StorePath {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        StorePathRef::from(self).fmt(f)
+    }
+}
+
+impl fmt::Display for StorePathRef<'_> {
     /// The string representation of a store path starts with a digest (20
     /// bytes), [crate::nixbase32]-encoded, followed by a `-`,
     /// and ends with the name.