about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/derivation/src/derivation.rs26
-rw-r--r--tvix/derivation/src/lib.rs2
-rw-r--r--tvix/derivation/src/tests/mod.rs36
3 files changed, 63 insertions, 1 deletions
diff --git a/tvix/derivation/src/derivation.rs b/tvix/derivation/src/derivation.rs
index 74e68ce1d7c5..1f9ed6146bbd 100644
--- a/tvix/derivation/src/derivation.rs
+++ b/tvix/derivation/src/derivation.rs
@@ -75,6 +75,32 @@ fn build_store_path(
     // invalid, so map errors to a [DerivationError::InvalidOutputName].
 }
 
+/// Build a store path for a literal text file in the store that may
+/// contain references.
+pub fn path_with_references<'a, I: IntoIterator<Item = &'a str>, C: AsRef<[u8]>>(
+    name: &str,
+    content: C,
+    references: I,
+) -> Result<StorePath, DerivationError> {
+    let mut hasher = Sha256::new();
+    hasher.update(content);
+    let content_hash = hasher.finalize_reset();
+
+    hasher.update("text");
+    for reference in references {
+        hasher.update(":");
+        hasher.update(reference);
+    }
+
+    hasher.update(&format!(":sha256:{:x}:", content_hash));
+    hasher.update(STORE_DIR);
+    hasher.update(&format!(":{}", name));
+
+    let digest = hasher.finalize();
+
+    build_store_path(false, &digest, name)
+}
+
 impl Derivation {
     pub fn serialize(&self, writer: &mut impl Write) -> Result<(), fmt::Error> {
         writer.write_str(write::DERIVATION_PREFIX)?;
diff --git a/tvix/derivation/src/lib.rs b/tvix/derivation/src/lib.rs
index 4f17c3906e4c..1b82251bf672 100644
--- a/tvix/derivation/src/lib.rs
+++ b/tvix/derivation/src/lib.rs
@@ -10,6 +10,6 @@ mod tests;
 
 // Public API of the crate.
 
-pub use derivation::Derivation;
+pub use derivation::{path_with_references, Derivation};
 pub use errors::{DerivationError, OutputError};
 pub use output::{Hash, Output};
diff --git a/tvix/derivation/src/tests/mod.rs b/tvix/derivation/src/tests/mod.rs
index 5dd60284f05c..ce39afdd2854 100644
--- a/tvix/derivation/src/tests/mod.rs
+++ b/tvix/derivation/src/tests/mod.rs
@@ -305,3 +305,39 @@ fn output_path_construction() {
             .expect("must succeed")
     );
 }
+
+#[test]
+fn path_with_zero_references() {
+    // This hash should match `builtins.toFile`, e.g.:
+    //
+    // nix-repl> builtins.toFile "foo" "bar"
+    // "/nix/store/vxjiwkjkn7x4079qvh1jkl5pn05j2aw0-foo"
+
+    let store_path = crate::path_with_references("foo", "bar", vec![])
+        .expect("path_with_references() should succeed");
+
+    assert_eq!(
+        store_path.to_absolute_path().as_str(),
+        "/nix/store/vxjiwkjkn7x4079qvh1jkl5pn05j2aw0-foo"
+    );
+}
+
+#[test]
+fn path_with_non_zero_references() {
+    // This hash should match:
+    //
+    // nix-repl> builtins.toFile "baz" "${builtins.toFile "foo" "bar"}"
+    // "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz"
+
+    let inner = crate::path_with_references("foo", "bar", vec![])
+        .expect("path_with_references() should succeed");
+    let inner_path = inner.to_absolute_path();
+
+    let outer = crate::path_with_references("baz", &inner_path, vec![inner_path.as_str()])
+        .expect("path_with_references() should succeed");
+
+    assert_eq!(
+        outer.to_absolute_path().as_str(),
+        "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz"
+    );
+}