From 50aa74a59c106a0e96a9d5c5bf6c0da979d5b33f Mon Sep 17 00:00:00 2001 From: edef Date: Fri, 27 Oct 2023 13:00:22 +0000 Subject: feat(nix-compat/store_path): add StorePathRef Change-Id: I0d888a55d93e5c22e77cb0264d09757656f731d7 Reviewed-on: https://cl.tvl.fyi/c/depot/+/9862 Tested-by: BuildkiteCI Reviewed-by: flokli --- tvix/nix-compat/src/store_path/mod.rs | 94 +++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 22 deletions(-) (limited to 'tvix') 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 { - // 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> 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 { + // 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. -- cgit 1.4.1