refactor(tvix/eval): use EvalIO::read_to_string in impure builtins
With this change, the behaviour of reading a string from a file path is controlled by the provided `EvalIO` structure. This is a huge step towards abstracting away I/O behaviour correctly. Change-Id: Ifde8e46cd863b16e0301dca45a434ad27560399f Reviewed-on: https://cl.tvl.fyi/c/depot/+/7567 Tested-by: BuildkiteCI Reviewed-by: grfn <grfn@gws.fyi>
This commit is contained in:
parent
c3c4d752c9
commit
0ef3c2fc2b
3 changed files with 21 additions and 38 deletions
|
@ -1,9 +1,7 @@
|
|||
use builtin_macros::builtins;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
env,
|
||||
fs::File,
|
||||
io::{self, Read},
|
||||
env, io,
|
||||
rc::{Rc, Weak},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
@ -68,9 +66,10 @@ mod impure_builtins {
|
|||
|
||||
#[builtin("readFile")]
|
||||
fn builtin_read_file(vm: &mut VM, path: Value) -> Result<Value, ErrorKind> {
|
||||
let mut buf = String::new();
|
||||
File::open(&coerce_value_to_path(&path, vm)?)?.read_to_string(&mut buf)?;
|
||||
Ok(buf.into())
|
||||
let path = coerce_value_to_path(&path, vm)?;
|
||||
vm.io()
|
||||
.read_to_string(path)
|
||||
.map(|s| Value::String(s.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,16 +121,12 @@ pub fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) -> Builti
|
|||
}
|
||||
|
||||
let current_span = vm.current_span();
|
||||
let entry = match vm.import_cache.entry(path.clone()) {
|
||||
std::collections::btree_map::Entry::Occupied(oe) => return Ok(oe.get().clone()),
|
||||
std::collections::btree_map::Entry::Vacant(ve) => ve,
|
||||
};
|
||||
|
||||
let contents =
|
||||
std::fs::read_to_string(&path).map_err(|err| ErrorKind::ReadFileError {
|
||||
path: path.clone(),
|
||||
error: Rc::new(err),
|
||||
})?;
|
||||
if let Some(cached) = vm.import_cache.get(&path) {
|
||||
return Ok(cached.clone());
|
||||
}
|
||||
|
||||
let contents = vm.io().read_to_string(path.clone())?;
|
||||
|
||||
let parsed = rnix::ast::Root::parse(&contents);
|
||||
let errors = parsed.errors();
|
||||
|
@ -174,12 +169,12 @@ pub fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) -> Builti
|
|||
|
||||
// Compilation succeeded, we can construct a thunk from whatever it spat
|
||||
// out and return that.
|
||||
let res = entry
|
||||
.insert(Value::Thunk(Thunk::new_suspended(
|
||||
result.lambda,
|
||||
LightSpan::new_actual(current_span),
|
||||
)))
|
||||
.clone();
|
||||
let res = Value::Thunk(Thunk::new_suspended(
|
||||
result.lambda,
|
||||
LightSpan::new_actual(current_span),
|
||||
));
|
||||
|
||||
vm.import_cache.insert(path, res.clone());
|
||||
|
||||
for warning in result.warnings {
|
||||
vm.push_warning(warning);
|
||||
|
|
|
@ -109,12 +109,6 @@ pub enum ErrorKind {
|
|||
/// literal attribute sets.
|
||||
UnmergeableValue,
|
||||
|
||||
/// Tvix failed to read a file from disk for some reason.
|
||||
ReadFileError {
|
||||
path: PathBuf,
|
||||
error: Rc<std::io::Error>,
|
||||
},
|
||||
|
||||
/// Parse errors occured while importing a file.
|
||||
ImportParseError {
|
||||
path: PathBuf,
|
||||
|
@ -342,15 +336,6 @@ to a missing value in the attribute set(s) included via `with`."#,
|
|||
)
|
||||
}
|
||||
|
||||
ErrorKind::ReadFileError { path, error } => {
|
||||
write!(
|
||||
f,
|
||||
"failed to read file '{}': {}",
|
||||
path.to_string_lossy(),
|
||||
error
|
||||
)
|
||||
}
|
||||
|
||||
// Errors themselves ignored here & handled in Self::spans instead
|
||||
ErrorKind::ImportParseError { path, .. } => {
|
||||
write!(
|
||||
|
@ -676,7 +661,6 @@ impl Error {
|
|||
| ErrorKind::NegativeLength { .. }
|
||||
| ErrorKind::UnmergeableInherit { .. }
|
||||
| ErrorKind::UnmergeableValue
|
||||
| ErrorKind::ReadFileError { .. }
|
||||
| ErrorKind::ImportParseError { .. }
|
||||
| ErrorKind::ImportCompilerError { .. }
|
||||
| ErrorKind::IO { .. }
|
||||
|
@ -716,7 +700,6 @@ impl Error {
|
|||
ErrorKind::TailEmptyList { .. } => "E023",
|
||||
ErrorKind::UnmergeableInherit { .. } => "E024",
|
||||
ErrorKind::UnmergeableValue => "E025",
|
||||
ErrorKind::ReadFileError { .. } => "E026",
|
||||
ErrorKind::ImportParseError { .. } => "E027",
|
||||
ErrorKind::ImportCompilerError { .. } => "E028",
|
||||
ErrorKind::IO { .. } => "E029",
|
||||
|
|
|
@ -220,6 +220,11 @@ impl<'o> VM<'o> {
|
|||
self.chunk().get_span(self.frame().ip - 1)
|
||||
}
|
||||
|
||||
/// Access the I/O handle used for filesystem access in this VM.
|
||||
pub(crate) fn io(&self) -> &Box<dyn EvalIO> {
|
||||
&self.io_handle
|
||||
}
|
||||
|
||||
/// Returns the information needed to calculate the current span,
|
||||
/// but without performing that calculation.
|
||||
fn current_light_span(&self) -> LightSpan {
|
||||
|
|
Loading…
Reference in a new issue