diff options
Diffstat (limited to 'tvix/nix-compat/src/nixhash')
-rw-r--r-- | tvix/nix-compat/src/nixhash/ca_hash.rs | 48 | ||||
-rw-r--r-- | tvix/nix-compat/src/nixhash/mod.rs | 29 |
2 files changed, 70 insertions, 7 deletions
diff --git a/tvix/nix-compat/src/nixhash/ca_hash.rs b/tvix/nix-compat/src/nixhash/ca_hash.rs index 93ef52cc63ab..0d6c68d7fa7f 100644 --- a/tvix/nix-compat/src/nixhash/ca_hash.rs +++ b/tvix/nix-compat/src/nixhash/ca_hash.rs @@ -10,13 +10,11 @@ use super::algos::SUPPORTED_ALGOS; use super::from_algo_and_digest; /// A Nix CAHash describes a content-addressed hash of a path. -/// Semantically, it can be split into the following components: /// -/// - "content address prefix". Currently, "fixed" and "text" are supported. -/// - "hash mode". Currently, "flat" and "recursive" are supported. -/// - "hash type". The underlying hash function used. -/// Currently, sha1, md5, sha256, sha512. -/// - "digest". The digest itself. +/// The way Nix prints it as a string is a bit confusing, but there's essentially +/// three modes, `Flat`, `Nar` and `Text`. +/// `Flat` and `Nar` support all 4 algos that [NixHash] supports +/// (sha1, md5, sha256, sha512), `Text` only supports sha256. #[derive(Clone, Debug, Eq, PartialEq)] pub enum CAHash { Flat(NixHash), // "fixed flat" @@ -33,6 +31,44 @@ impl CAHash { } } + /// Constructs a [CAHash] from the textual representation, + /// which is one of the three: + /// - `text:sha256:$nixbase32sha256digest` + /// - `fixed:r:$algo:$nixbase32digest` + /// - `fixed:$algo:$nixbase32digest` + /// which is the format that's used in the NARInfo for example. + pub fn from_nix_hex_str(s: &str) -> Option<Self> { + let (tag, s) = s.split_once(':')?; + + match tag { + "text" => { + let digest = s.strip_prefix("sha256:")?; + let digest = nixbase32::decode_fixed(digest).ok()?; + Some(CAHash::Text(digest)) + } + "fixed" => { + if let Some(s) = s.strip_prefix("r:") { + NixHash::from_nix_hex_str(s).map(CAHash::Nar) + } else { + NixHash::from_nix_hex_str(s).map(CAHash::Flat) + } + } + _ => None, + } + } + + /// Formats a [CAHash] in the Nix default hash format, which is the format + /// that's used in NARInfos for example. + pub fn to_nix_nixbase32_string(&self) -> String { + match self { + CAHash::Flat(nh) => format!("fixed:{}", nh.to_nix_nixbase32_string()), + CAHash::Nar(nh) => format!("fixed:r:{}", nh.to_nix_nixbase32_string()), + CAHash::Text(digest) => { + format!("text:sha256:{}", nixbase32::encode(digest)) + } + } + } + /// This takes a serde_json::Map and turns it into this structure. This is necessary to do such /// shenigans because we have external consumers, like the Derivation parser, who would like to /// know whether we have a invalid or a missing NixHashWithMode structure in another structure, diff --git a/tvix/nix-compat/src/nixhash/mod.rs b/tvix/nix-compat/src/nixhash/mod.rs index f699a4cd9524..a93d2b68da94 100644 --- a/tvix/nix-compat/src/nixhash/mod.rs +++ b/tvix/nix-compat/src/nixhash/mod.rs @@ -41,15 +41,42 @@ impl NixHash { } } + /// Constructs a [NixHash] from the Nix default hash format, + /// the inverse of [to_nix_hex_string]. + pub fn from_nix_hex_str(s: &str) -> Option<Self> { + let (tag, digest) = s.split_once(':')?; + + (match tag { + "md5" => nixbase32::decode_fixed(digest).map(NixHash::Md5), + "sha1" => nixbase32::decode_fixed(digest).map(NixHash::Sha1), + "sha256" => nixbase32::decode_fixed(digest).map(NixHash::Sha256), + "sha512" => nixbase32::decode_fixed(digest) + .map(Box::new) + .map(NixHash::Sha512), + _ => return None, + }) + .ok() + } + /// Formats a [NixHash] in the Nix default hash format, /// which is the algo, followed by a colon, then the lower hex encoded digest. - pub fn to_nix_hash_string(&self) -> String { + pub fn to_nix_hex_string(&self) -> String { format!( "{}:{}", self.algo(), HEXLOWER.encode(self.digest_as_bytes()) ) } + + /// Formats a [NixHash] in the format that's used inside CAHash, + /// which is the algo, followed by a colon, then the nixbase32-encoded digest. + pub(crate) fn to_nix_nixbase32_string(&self) -> String { + format!( + "{}:{}", + self.algo(), + nixbase32::encode(self.digest_as_bytes()) + ) + } } impl TryFrom<(HashAlgo, &[u8])> for NixHash { |