feat(tvix/eval): Support builtins.readDir
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
This commit is contained in:
parent
899fbdbddb
commit
41ddc37725
7 changed files with 73 additions and 2 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
1
tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp
Normal file
1
tvix/eval/src/tests/tvix_tests/eval-okay-readDir.exp
Normal file
|
@ -0,0 +1 @@
|
|||
{ bar = "regular"; foo = "directory"; }
|
1
tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix
Normal file
1
tvix/eval/src/tests/tvix_tests/eval-okay-readDir.nix
Normal file
|
@ -0,0 +1 @@
|
|||
builtins.readDir ./readDir
|
0
tvix/eval/src/tests/tvix_tests/readDir/bar
Normal file
0
tvix/eval/src/tests/tvix_tests/readDir/bar
Normal file
0
tvix/eval/src/tests/tvix_tests/readDir/foo/.keep
Normal file
0
tvix/eval/src/tests/tvix_tests/readDir/foo/.keep
Normal file
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue