feat(tvix/eval): implement Value::coerce_to_path()
This function is necessary for all builtins that expect some form of path as an argument. It is merely a wrapper around coerce_to_string that can shortcut if we already have a path. The absolute path check is done in the same way as in C++ Nix for compatibility, although it should probably be revised in the long term (think about Windows, for example). Since coercing to a path is not an operation possible in the language directly, this function can live in the builtins module as the only place it is required. Change-Id: I69ed5455c00d193fea88b8fa83e28907a761cab5 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6574 Autosubmit: sterni <sternenseemann@systemli.org> Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
This commit is contained in:
parent
e834a2cbc4
commit
067f2b16f6
4 changed files with 48 additions and 1 deletions
|
@ -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).
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::value::CoercionKind;
|
||||
use std::fmt::Display;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codemap::{CodeMap, Span};
|
||||
use codemap_diagnostic::{Diagnostic, Emitter, Level, SpanLabel, SpanStyle};
|
||||
|
@ -73,6 +74,9 @@ pub enum ErrorKind {
|
|||
kind: CoercionKind,
|
||||
},
|
||||
|
||||
/// The given string doesn't represent an absolute path
|
||||
NotAnAbsolutePath(PathBuf),
|
||||
|
||||
/// Tvix internal warning for features triggered by users that are
|
||||
/// not actually implemented yet, and without which eval can not
|
||||
/// proceed.
|
||||
|
@ -189,6 +193,13 @@ to a missing value in the attribute set(s) included via `with`."#,
|
|||
format!("cannot ({kindly}) coerce {from} to a string{hint}")
|
||||
}
|
||||
|
||||
ErrorKind::NotAnAbsolutePath(given) => {
|
||||
format!(
|
||||
"string {} doesn't represent an absolute path",
|
||||
given.to_string_lossy()
|
||||
)
|
||||
}
|
||||
|
||||
ErrorKind::NotImplemented(feature) => {
|
||||
format!("feature not yet implemented in Tvix: {}", feature)
|
||||
}
|
||||
|
@ -218,6 +229,7 @@ to a missing value in the attribute set(s) included via `with`."#,
|
|||
ErrorKind::ThunkForce(_) => "E017",
|
||||
ErrorKind::NotCoercibleToString { .. } => "E018",
|
||||
ErrorKind::IndexOutOfBounds { .. } => "E019",
|
||||
ErrorKind::NotAnAbsolutePath(_) => "E020",
|
||||
ErrorKind::NotImplemented(_) => "E999",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,8 @@ impl Value {
|
|||
kind: CoercionKind,
|
||||
vm: &mut VM,
|
||||
) -> Result<NixString, ErrorKind> {
|
||||
// TODO: eventually, this will need to handle string context and importing
|
||||
// files into the Nix store depending on what context the coercion happens in
|
||||
if let Value::Thunk(t) = self {
|
||||
t.force(vm)?;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//! backing implementations.
|
||||
use smol_str::SmolStr;
|
||||
use std::hash::Hash;
|
||||
use std::{borrow::Cow, fmt::Display};
|
||||
use std::{borrow::Cow, fmt::Display, str::Chars};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum StringRepr {
|
||||
|
@ -97,6 +97,13 @@ impl NixString {
|
|||
s.push_str(other.as_str());
|
||||
NixString(StringRepr::Heap(s))
|
||||
}
|
||||
|
||||
pub fn chars(&self) -> Chars<'_> {
|
||||
match &self.0 {
|
||||
StringRepr::Heap(h) => h.chars(),
|
||||
StringRepr::Smol(s) => s.chars(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nix_escape_char(ch: char, next: Option<&char>) -> Option<&'static str> {
|
||||
|
|
Loading…
Reference in a new issue