about summary refs log tree commit diff
path: root/tvix/eval/src/builtins/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/builtins/mod.rs')
-rw-r--r--tvix/eval/src/builtins/mod.rs26
1 files changed, 26 insertions, 0 deletions
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs
index 33abfe492d10..7a2f46e2d47d 100644
--- a/tvix/eval/src/builtins/mod.rs
+++ b/tvix/eval/src/builtins/mod.rs
@@ -5,12 +5,14 @@
 
 use std::{
     collections::{BTreeMap, HashMap},
+    path::PathBuf,
     rc::Rc,
 };
 
 use crate::{
     errors::ErrorKind,
     value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Value},
+    vm::VM,
 };
 
 use crate::arithmetic_op;
@@ -36,6 +38,30 @@ macro_rules! force {
     };
 }
 
+/// Coerce a Nix Value to a plain path, e.g. in order to access the file it
+/// points to in an I/O builtin. This coercion can _never_ be performed in
+/// a Nix program directly (i.e. the trick `path: /. + path` to convert from
+/// a string to a path wouldn't hit this code), so the target file
+/// doesn't need to be realised or imported into the Nix store.
+pub fn coerce_value_to_path(v: &Value, vm: &mut VM) -> Result<PathBuf, ErrorKind> {
+    force!(vm, v, value, {
+        match value {
+            Value::Thunk(t) => coerce_value_to_path(&t.value(), vm),
+            Value::Path(p) => Ok(p.clone()),
+            _ => value
+                .coerce_to_string(CoercionKind::Weak, vm)
+                .map(|s| PathBuf::from(s.as_str()))
+                .and_then(|path| {
+                    if path.is_absolute() {
+                        Ok(path)
+                    } else {
+                        Err(ErrorKind::NotAnAbsolutePath(path))
+                    }
+                }),
+        }
+    })
+}
+
 /// Return all pure builtins, that is all builtins that do not rely on
 /// I/O outside of the VM and which can be used in any contexts (e.g.
 /// WASM).