about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-12-13T17·17+0300
committertazjin <tazjin@tvl.su>2022-12-22T19·09+0000
commitea7d63e177d8a951e30bc00918081eb069e2efb2 (patch)
tree9177f7ff683f2eeef178df2aeda349e52b915c05
parent4714f8b93998bee9c1b656f0b2358a26d3cf899d (diff)
feat(tvix/cli): implement `NixCompatIO` helper type r/5474
This type allows for temporarily compatibility with the C++ Nix store,
specifically (for now) it gives us the store directory used by Nix and
imports files the same way.

Change-Id: I4767794ef2863eba49661315c63c4e17de946d60
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7587
Reviewed-by: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
-rw-r--r--tvix/Cargo.lock1
-rw-r--r--tvix/Cargo.nix4
-rw-r--r--tvix/cli/Cargo.toml1
-rw-r--r--tvix/cli/src/main.rs4
-rw-r--r--tvix/cli/src/nix_compat.rs82
-rw-r--r--tvix/eval/src/lib.rs2
6 files changed, 92 insertions, 2 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock
index b6815b2459..6724b3d58f 100644
--- a/tvix/Cargo.lock
+++ b/tvix/Cargo.lock
@@ -1493,6 +1493,7 @@ dependencies = [
  "clap 4.0.27",
  "dirs",
  "rustyline",
+ "smol_str",
  "tvix-eval",
 ]
 
diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix
index 1447c4ea17..cce3bbca83 100644
--- a/tvix/Cargo.nix
+++ b/tvix/Cargo.nix
@@ -4230,6 +4230,10 @@ rec {
             packageId = "rustyline";
           }
           {
+            name = "smol_str";
+            packageId = "smol_str";
+          }
+          {
             name = "tvix-eval";
             packageId = "tvix-eval";
           }
diff --git a/tvix/cli/Cargo.toml b/tvix/cli/Cargo.toml
index ae7cd22210..099002353d 100644
--- a/tvix/cli/Cargo.toml
+++ b/tvix/cli/Cargo.toml
@@ -12,3 +12,4 @@ tvix-eval = { path = "../eval" }
 rustyline = "10.0.0"
 clap = { version = "4.0", features = ["derive", "env"] }
 dirs = "4.0.0"
+smol_str = "0.1"
diff --git a/tvix/cli/src/main.rs b/tvix/cli/src/main.rs
index 7e6b7520b0..3228489e71 100644
--- a/tvix/cli/src/main.rs
+++ b/tvix/cli/src/main.rs
@@ -1,3 +1,5 @@
+mod nix_compat;
+
 use std::{fs, path::PathBuf};
 
 use clap::Parser;
@@ -39,7 +41,7 @@ struct Args {
 /// evaluation succeeded.
 fn interpret(code: &str, path: Option<PathBuf>, args: &Args) -> bool {
     let mut eval = tvix_eval::Evaluation::new(code, path);
-    eval.io_handle = Box::new(tvix_eval::StdIO);
+    eval.io_handle = Box::new(nix_compat::NixCompatIO::new());
     eval.nix_path = args.nix_search_path.clone();
 
     let source_map = eval.source_map();
diff --git a/tvix/cli/src/nix_compat.rs b/tvix/cli/src/nix_compat.rs
new file mode 100644
index 0000000000..75281d90e8
--- /dev/null
+++ b/tvix/cli/src/nix_compat.rs
@@ -0,0 +1,82 @@
+//! This module implements (temporary) compatibility shims between
+//! Tvix and C++ Nix.
+//!
+//! These are not intended to be long-lived, but should bootstrap Tvix
+//! by piggybacking off functionality that already exists in Nix and
+//! is still being implemented in Tvix.
+
+use std::path::Path;
+use std::process::Command;
+use std::{io, path::PathBuf};
+
+use smol_str::SmolStr;
+use tvix_eval::{ErrorKind, EvalIO, FileType, StdIO};
+
+/// Compatibility implementation of [`EvalIO`] that uses C++ Nix to
+/// write files to the Nix store.
+pub struct NixCompatIO {
+    /// Most IO requests are tunneled through to [`tvix_eval::StdIO`]
+    /// instead.
+    underlying: StdIO,
+}
+
+impl NixCompatIO {
+    pub fn new() -> Self {
+        NixCompatIO { underlying: StdIO }
+    }
+}
+
+impl EvalIO for NixCompatIO {
+    fn store_dir(&self) -> Option<String> {
+        Some("/nix/store".into())
+    }
+
+    // Pass path imports through to `nix-store --add`
+    fn import_path(&self, path: &Path) -> Result<PathBuf, ErrorKind> {
+        add_to_store(path).map_err(|error| ErrorKind::IO {
+            error: std::rc::Rc::new(error),
+            path: Some(path.to_path_buf()),
+        })
+    }
+
+    // Pass the rest of the functions through to `Self::underlying`
+    fn path_exists(&self, path: PathBuf) -> Result<bool, ErrorKind> {
+        self.underlying.path_exists(path)
+    }
+
+    fn read_to_string(&self, path: PathBuf) -> Result<String, ErrorKind> {
+        self.underlying.read_to_string(path)
+    }
+
+    fn read_dir(&self, path: PathBuf) -> Result<Vec<(SmolStr, FileType)>, ErrorKind> {
+        self.underlying.read_dir(path)
+    }
+}
+
+/// Add a path to the Nix store using the `nix-store --add`
+/// functionality from C++ Nix.
+fn add_to_store(path: &Path) -> Result<PathBuf, io::Error> {
+    if !path.try_exists()? {
+        return Err(io::Error::from(io::ErrorKind::NotFound));
+    }
+
+    let mut cmd = Command::new("nix-store");
+    cmd.arg("--add");
+    cmd.arg(path);
+
+    let out = cmd.output()?;
+
+    if !out.status.success() {
+        return Err(io::Error::new(
+            io::ErrorKind::Other,
+            String::from_utf8_lossy(&out.stderr),
+        ));
+    }
+
+    let out_path_str = String::from_utf8(out.stdout)
+        .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+
+    let mut out_path = PathBuf::new();
+    out_path.push(out_path_str.trim());
+    Ok(out_path)
+}
diff --git a/tvix/eval/src/lib.rs b/tvix/eval/src/lib.rs
index d8620a145e..bf6c014262 100644
--- a/tvix/eval/src/lib.rs
+++ b/tvix/eval/src/lib.rs
@@ -45,7 +45,7 @@ use std::sync::Arc;
 pub use crate::builtins::global_builtins;
 pub use crate::compiler::{compile, prepare_globals};
 pub use crate::errors::{Error, ErrorKind, EvalResult};
-pub use crate::io::{DummyIO, EvalIO};
+pub use crate::io::{DummyIO, EvalIO, FileType};
 use crate::observer::{CompilerObserver, RuntimeObserver};
 pub use crate::pretty_ast::pretty_print_expr;
 pub use crate::source::SourceCode;