about summary refs log tree commit diff
path: root/tvix/nix-compat/src/nixhash/ca_hash.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/nix-compat/src/nixhash/ca_hash.rs')
-rw-r--r--tvix/nix-compat/src/nixhash/ca_hash.rs48
1 files changed, 42 insertions, 6 deletions
diff --git a/tvix/nix-compat/src/nixhash/ca_hash.rs b/tvix/nix-compat/src/nixhash/ca_hash.rs
index 93ef52cc63..0d6c68d7fa 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,