diff options
author | William Carroll <wpcarro@gmail.com> | 2022-09-06T20·56-0700 |
---|---|---|
committer | wpcarro <wpcarro@gmail.com> | 2022-10-10T19·36+0000 |
commit | 41ddc37725a59b4af0f7043a7fd66b3fe48f935d (patch) | |
tree | 09cc3a4b6456546697d0d28f91f80be74b75ce00 | |
parent | 899fbdbddb93050f26236ff7c72e7ae4704d497b (diff) |
feat(tvix/eval): Support builtins.readDir r/5083
Co-authored-by: Griffin Smith <root@gws.fyi> Change-Id: I5ff19efbe87d8f571f22ab0480500505afa624c5 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6552 Autosubmit: wpcarro <wpcarro@gmail.com> Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
-rw-r--r-- | tvix/eval/src/builtins/impure.rs | 41 | ||||
-rw-r--r-- | tvix/eval/src/errors.rs | 26 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp | 1 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix | 1 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/readDir/bar | 0 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/readDir/foo/.keep | 0 | ||||
-rw-r--r-- | tvix/eval/src/value/mod.rs | 6 |
7 files changed, 73 insertions, 2 deletions
diff --git a/tvix/eval/src/builtins/impure.rs b/tvix/eval/src/builtins/impure.rs index 8e23003edb47..32653b665c36 100644 --- a/tvix/eval/src/builtins/impure.rs +++ b/tvix/eval/src/builtins/impure.rs @@ -1,6 +1,7 @@ use std::{ cell::RefCell, collections::{BTreeMap, HashMap}, + io, rc::Rc, time::{SystemTime, UNIX_EPOCH}, }; @@ -8,13 +9,49 @@ use std::{ use crate::{ errors::ErrorKind, observer::NoOpObserver, - value::{Builtin, NixString, Thunk}, + value::{Builtin, NixAttrs, NixString, Thunk}, vm::VM, SourceCode, Value, }; fn impure_builtins() -> Vec<Builtin> { - vec![] + vec![Builtin::new( + "readDir", + &[true], + |args: Vec<Value>, vm: &mut VM| { + let path = super::coerce_value_to_path(&args[0], vm)?; + let mk_err = |err: io::Error| ErrorKind::IO { + path: Some(path.clone()), + error: Rc::new(err), + }; + + let mut res = BTreeMap::new(); + for entry in path.read_dir().map_err(mk_err)? { + let entry = entry.map_err(mk_err)?; + let file_type = entry + .metadata() + .map_err(|err| ErrorKind::IO { + path: Some(entry.path()), + error: Rc::new(err), + })? + .file_type(); + let val = if file_type.is_dir() { + "directory" + } else if file_type.is_file() { + "regular" + } else if file_type.is_symlink() { + "symlink" + } else { + "unknown" + }; + res.insert( + entry.file_name().to_string_lossy().as_ref().into(), + val.into(), + ); + } + Ok(Value::attrs(NixAttrs::from_map(res))) + }, + )] } /// Return all impure builtins, that is all builtins which may perform I/O diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index a2f09e98fb2e..a7d54d51bb27 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -1,5 +1,6 @@ use crate::spans::ToSpan; use crate::value::CoercionKind; +use std::io; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -121,6 +122,12 @@ pub enum ErrorKind { errors: Vec<Error>, }, + /// I/O errors + IO { + path: Option<PathBuf>, + error: Rc<io::Error>, + }, + /// Tvix internal warning for features triggered by users that are /// not actually implemented yet, and without which eval can not /// proceed. @@ -142,6 +149,15 @@ impl From<Error> for ErrorKind { } } +impl From<io::Error> for ErrorKind { + fn from(e: io::Error) -> Self { + ErrorKind::IO { + path: None, + error: Rc::new(e), + } + } +} + #[derive(Clone, Debug)] pub struct Error { pub kind: ErrorKind, @@ -306,6 +322,14 @@ to a missing value in the attribute set(s) included via `with`."#, ) } + ErrorKind::IO { path, error } => { + write!(f, "I/O error: ")?; + if let Some(path) = path { + write!(f, "{}: ", path.display())?; + } + write!(f, "{error}") + } + ErrorKind::NotImplemented(feature) => { write!(f, "feature not yet implemented in Tvix: {}", feature) } @@ -583,6 +607,7 @@ impl Error { | ErrorKind::ReadFileError { .. } | ErrorKind::ImportParseError { .. } | ErrorKind::ImportCompilerError { .. } + | ErrorKind::IO { .. } | ErrorKind::NotImplemented(_) => return None, }; @@ -620,6 +645,7 @@ impl Error { ErrorKind::ReadFileError { .. } => "E026", ErrorKind::ImportParseError { .. } => "E027", ErrorKind::ImportCompilerError { .. } => "E028", + ErrorKind::IO { .. } => "E029", // Placeholder error while Tvix is under construction. ErrorKind::NotImplemented(_) => "E999", diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp new file mode 100644 index 000000000000..bf8d2c14ea4f --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp @@ -0,0 +1 @@ +{ bar = "regular"; foo = "directory"; } diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix new file mode 100644 index 000000000000..a7ec9292aae2 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix @@ -0,0 +1 @@ +builtins.readDir ./readDir diff --git a/tvix/eval/src/tests/tvix_tests/readDir/bar b/tvix/eval/src/tests/tvix_tests/readDir/bar new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/readDir/bar diff --git a/tvix/eval/src/tests/tvix_tests/readDir/foo/.keep b/tvix/eval/src/tests/tvix_tests/readDir/foo/.keep new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/readDir/foo/.keep diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs index d3b981b78cba..0f3b9cbbf4a2 100644 --- a/tvix/eval/src/value/mod.rs +++ b/tvix/eval/src/value/mod.rs @@ -371,6 +371,12 @@ impl From<i64> for Value { } } +impl From<&str> for Value { + fn from(val: &str) -> Self { + Self::String(val.into()) + } +} + fn type_error(expected: &'static str, actual: &Value) -> ErrorKind { ErrorKind::TypeError { expected, |