about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/Cargo.lock15
-rw-r--r--tvix/Cargo.nix57
-rw-r--r--tvix/nix-compat/Cargo.toml8
-rw-r--r--tvix/nix-compat/src/lib.rs1
-rw-r--r--tvix/nix-compat/src/wire/mod.rs5
-rw-r--r--tvix/nix-compat/src/wire/primitive.rs75
6 files changed, 157 insertions, 4 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock
index 9f59ebe54f73..a10b4b35fa4d 100644
--- a/tvix/Cargo.lock
+++ b/tvix/Cargo.lock
@@ -1723,6 +1723,8 @@ dependencies = [
  "test-case",
  "test-generator",
  "thiserror",
+ "tokio",
+ "tokio-test",
  "zstd",
 ]
 
@@ -3271,6 +3273,19 @@ dependencies = [
 ]
 
 [[package]]
+name = "tokio-test"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719"
+dependencies = [
+ "async-stream",
+ "bytes",
+ "futures-core",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
 name = "tokio-util"
 version = "0.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix
index 6fc42a71b59f..258461ffe427 100644
--- a/tvix/Cargo.nix
+++ b/tvix/Cargo.nix
@@ -5149,6 +5149,12 @@ rec {
             name = "thiserror";
             packageId = "thiserror";
           }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "io-util" "macros" ];
+          }
         ];
         devDependencies = [
           {
@@ -5187,15 +5193,20 @@ rec {
             packageId = "test-generator";
           }
           {
+            name = "tokio-test";
+            packageId = "tokio-test";
+          }
+          {
             name = "zstd";
             packageId = "zstd";
           }
         ];
         features = {
-          "async" = [ "futures-util" ];
+          "async" = [ "futures-util" "tokio" ];
           "futures-util" = [ "dep:futures-util" ];
+          "tokio" = [ "dep:tokio" ];
         };
-        resolvedDefaultFeatures = [ "async" "futures-util" ];
+        resolvedDefaultFeatures = [ "async" "futures-util" "tokio" ];
       };
       "nom" = rec {
         crateName = "nom";
@@ -9687,7 +9698,7 @@ rec {
           "tracing" = [ "dep:tracing" ];
           "windows-sys" = [ "dep:windows-sys" ];
         };
-        resolvedDefaultFeatures = [ "bytes" "default" "fs" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "time" "tokio-macros" "windows-sys" ];
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "test-util" "time" "tokio-macros" "windows-sys" ];
       };
       "tokio-io-timeout" = rec {
         crateName = "tokio-io-timeout";
@@ -9995,6 +10006,46 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "xattr" ];
       };
+      "tokio-test" = rec {
+        crateName = "tokio-test";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "06fplzcc2ymahfzykd2ickw2qn7g3lz47bll00865s1spnx3r6z8";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt" "sync" "time" "test-util" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+
+      };
       "tokio-util" = rec {
         crateName = "tokio-util";
         version = "0.7.10";
diff --git a/tvix/nix-compat/Cargo.toml b/tvix/nix-compat/Cargo.toml
index c4672ff9fb9e..181eb9428915 100644
--- a/tvix/nix-compat/Cargo.toml
+++ b/tvix/nix-compat/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [features]
-async = ["futures-util"]
+async = ["futures-util", "tokio"]
 
 [dependencies]
 bitflags = "2.4.1"
@@ -22,6 +22,11 @@ serde_json = "1.0"
 sha2 = "0.10.6"
 thiserror = "1.0.38"
 
+[dependencies.tokio]
+optional = true
+version = "1.32.0"
+features = ["io-util", "macros"]
+
 [dev-dependencies]
 futures = { version = "0.3.30", default-features = false, features = ["executor"] }
 lazy_static = "1.4.0"
@@ -30,6 +35,7 @@ test-case = "3.3.1"
 criterion = { version = "0.5", features = ["html_reports"] }
 hex-literal = "0.4.1"
 pretty_assertions = "1.4.0"
+tokio-test = "0.4.3"
 zstd = "^0.13.0"
 
 [dev-dependencies.test-generator]
diff --git a/tvix/nix-compat/src/lib.rs b/tvix/nix-compat/src/lib.rs
index dd161cc1f944..60dcbdf25caa 100644
--- a/tvix/nix-compat/src/lib.rs
+++ b/tvix/nix-compat/src/lib.rs
@@ -5,3 +5,4 @@ pub mod narinfo;
 pub mod nixbase32;
 pub mod nixhash;
 pub mod store_path;
+mod wire;
diff --git a/tvix/nix-compat/src/wire/mod.rs b/tvix/nix-compat/src/wire/mod.rs
new file mode 100644
index 000000000000..e0b184c78aec
--- /dev/null
+++ b/tvix/nix-compat/src/wire/mod.rs
@@ -0,0 +1,5 @@
+//! Module parsing and emitting the wire format used by Nix, both in the
+//! nix-daemon protocol as well as in the NAR format.
+
+#[cfg(feature = "async")]
+pub mod primitive;
diff --git a/tvix/nix-compat/src/wire/primitive.rs b/tvix/nix-compat/src/wire/primitive.rs
new file mode 100644
index 000000000000..54f00b15a0c0
--- /dev/null
+++ b/tvix/nix-compat/src/wire/primitive.rs
@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: 2023 embr <git@liclac.eu>
+//
+// SPDX-License-Identifier: EUPL-1.2
+
+use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
+
+#[allow(dead_code)]
+/// Read a u64 from the stream (little endian).
+pub async fn read_u64<R: AsyncReadExt + Unpin>(r: &mut R) -> std::io::Result<u64> {
+    r.read_u64_le().await
+}
+
+#[allow(dead_code)]
+/// Write a u64 from the stream (little endian).
+pub async fn write_u64<W: AsyncWrite + Unpin>(w: &mut W, v: u64) -> std::io::Result<()> {
+    w.write_u64_le(v).await
+}
+
+#[allow(dead_code)]
+/// Read a boolean from the stream, encoded as u64 (>0 is true).
+pub async fn read_bool<R: AsyncRead + Unpin>(r: &mut R) -> std::io::Result<bool> {
+    Ok(read_u64(r).await? > 0)
+}
+
+#[allow(dead_code)]
+/// Write a boolean to the stream, encoded as u64 (>0 is true).
+pub async fn write_bool<W: AsyncWrite + Unpin>(w: &mut W, v: bool) -> std::io::Result<()> {
+    write_u64(w, if v { 1u64 } else { 0u64 }).await
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use tokio_test::io::Builder;
+
+    // Integers.
+    #[tokio::test]
+    async fn test_read_u64() {
+        let mut mock = Builder::new().read(&1234567890u64.to_le_bytes()).build();
+        assert_eq!(1234567890u64, read_u64(&mut mock).await.unwrap());
+    }
+    #[tokio::test]
+    async fn test_write_u64() {
+        let mut mock = Builder::new().write(&1234567890u64.to_le_bytes()).build();
+        write_u64(&mut mock, 1234567890).await.unwrap();
+    }
+
+    // Booleans.
+    #[tokio::test]
+    async fn test_read_bool_0() {
+        let mut mock = Builder::new().read(&0u64.to_le_bytes()).build();
+        assert!(!read_bool(&mut mock).await.unwrap());
+    }
+    #[tokio::test]
+    async fn test_read_bool_1() {
+        let mut mock = Builder::new().read(&1u64.to_le_bytes()).build();
+        assert!(read_bool(&mut mock).await.unwrap());
+    }
+    #[tokio::test]
+    async fn test_read_bool_2() {
+        let mut mock = Builder::new().read(&2u64.to_le_bytes()).build();
+        assert!(read_bool(&mut mock).await.unwrap());
+    }
+
+    #[tokio::test]
+    async fn test_write_bool_false() {
+        let mut mock = Builder::new().write(&0u64.to_le_bytes()).build();
+        write_bool(&mut mock, false).await.unwrap();
+    }
+    #[tokio::test]
+    async fn test_write_bool_true() {
+        let mut mock = Builder::new().write(&1u64.to_le_bytes()).build();
+        write_bool(&mut mock, true).await.unwrap();
+    }
+}