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::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
|
path::PathBuf,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::ErrorKind,
|
errors::ErrorKind,
|
||||||
value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Value},
|
value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Value},
|
||||||
|
vm::VM,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::arithmetic_op;
|
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
|
/// 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.
|
/// I/O outside of the VM and which can be used in any contexts (e.g.
|
||||||
/// WASM).
|
/// WASM).
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::value::CoercionKind;
|
use crate::value::CoercionKind;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use codemap::{CodeMap, Span};
|
use codemap::{CodeMap, Span};
|
||||||
use codemap_diagnostic::{Diagnostic, Emitter, Level, SpanLabel, SpanStyle};
|
use codemap_diagnostic::{Diagnostic, Emitter, Level, SpanLabel, SpanStyle};
|
||||||
|
@ -73,6 +74,9 @@ pub enum ErrorKind {
|
||||||
kind: CoercionKind,
|
kind: CoercionKind,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// The given string doesn't represent an absolute path
|
||||||
|
NotAnAbsolutePath(PathBuf),
|
||||||
|
|
||||||
/// Tvix internal warning for features triggered by users that are
|
/// Tvix internal warning for features triggered by users that are
|
||||||
/// not actually implemented yet, and without which eval can not
|
/// not actually implemented yet, and without which eval can not
|
||||||
/// proceed.
|
/// 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}")
|
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) => {
|
ErrorKind::NotImplemented(feature) => {
|
||||||
format!("feature not yet implemented in Tvix: {}", 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::ThunkForce(_) => "E017",
|
||||||
ErrorKind::NotCoercibleToString { .. } => "E018",
|
ErrorKind::NotCoercibleToString { .. } => "E018",
|
||||||
ErrorKind::IndexOutOfBounds { .. } => "E019",
|
ErrorKind::IndexOutOfBounds { .. } => "E019",
|
||||||
|
ErrorKind::NotAnAbsolutePath(_) => "E020",
|
||||||
ErrorKind::NotImplemented(_) => "E999",
|
ErrorKind::NotImplemented(_) => "E999",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,8 @@ impl Value {
|
||||||
kind: CoercionKind,
|
kind: CoercionKind,
|
||||||
vm: &mut VM,
|
vm: &mut VM,
|
||||||
) -> Result<NixString, ErrorKind> {
|
) -> 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 {
|
if let Value::Thunk(t) = self {
|
||||||
t.force(vm)?;
|
t.force(vm)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//! backing implementations.
|
//! backing implementations.
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::{borrow::Cow, fmt::Display};
|
use std::{borrow::Cow, fmt::Display, str::Chars};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum StringRepr {
|
enum StringRepr {
|
||||||
|
@ -97,6 +97,13 @@ impl NixString {
|
||||||
s.push_str(other.as_str());
|
s.push_str(other.as_str());
|
||||||
NixString(StringRepr::Heap(s))
|
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> {
|
fn nix_escape_char(ch: char, next: Option<&char>) -> Option<&'static str> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue