feat(tvix/glue): Implement builtins.storePath

This one's relatively simple - we just check if the store path exists,
and if it does we make a new contextful string containing the store path
as its only context element.

Automatic testing seems tricky for this (I think?) so I tested it
manually:

tvix-repl> builtins.storePath /nix/store/yn46i4xx5alh7gs6fpkxk430i34rp2q9-hello-2.12.1
=> "/nix/store/yn46i4xx5alh7gs6fpkxk430i34rp2q9-hello-2.12.1" :: string

Change-Id: I8a0d9726e4102ab872c53c2419679c2c855a5a18
Reviewed-on: https://cl.tvl.fyi/c/depot/+/11696
Tested-by: BuildkiteCI
Autosubmit: aspen <root@gws.fyi>
Reviewed-by: flokli <flokli@flokli.de>
This commit is contained in:
Aspen Smith 2024-05-19 20:43:46 -04:00 committed by clbot
parent c3572048d5
commit 72b9a126b8
7 changed files with 62 additions and 2 deletions

View file

@ -54,6 +54,12 @@ impl From<HashSet<NixContextElement>> for NixContext {
} }
} }
impl<const N: usize> From<[NixContextElement; N]> for NixContext {
fn from(value: [NixContextElement; N]) -> Self {
Self(HashSet::from(value))
}
}
impl NixContext { impl NixContext {
/// Creates an empty context that can be populated /// Creates an empty context that can be populated
/// and passed to form a contextful [NixString], albeit /// and passed to form a contextful [NixString], albeit

View file

@ -4,7 +4,7 @@ use nix_compat::{
store_path::BuildStorePathError, store_path::BuildStorePathError,
}; };
use reqwest::Url; use reqwest::Url;
use std::rc::Rc; use std::{path::PathBuf, rc::Rc};
use thiserror::Error; use thiserror::Error;
use tvix_castore::import; use tvix_castore::import;
@ -65,8 +65,12 @@ pub enum FetcherError {
pub enum ImportError { pub enum ImportError {
#[error("non-file '{0}' cannot be imported in 'flat' mode")] #[error("non-file '{0}' cannot be imported in 'flat' mode")]
FlatImportOfNonFile(String), FlatImportOfNonFile(String),
#[error("hash mismatch at ingestion of '{0}', expected: '{1}', got: '{2}'")] #[error("hash mismatch at ingestion of '{0}', expected: '{1}', got: '{2}'")]
HashMismatch(String, NixHash, NixHash), HashMismatch(String, NixHash, NixHash),
#[error("path '{}' is not in the Nix store", .0.display())]
PathNotInStore(PathBuf),
} }
impl From<ImportError> for tvix_eval::ErrorKind { impl From<ImportError> for tvix_eval::ErrorKind {

View file

@ -104,11 +104,13 @@ async fn filtered_ingest(
#[builtins(state = "Rc<TvixStoreIO>")] #[builtins(state = "Rc<TvixStoreIO>")]
mod import_builtins { mod import_builtins {
use std::os::unix::ffi::OsStrExt;
use std::rc::Rc; use std::rc::Rc;
use super::*; use super::*;
use nix_compat::nixhash::{CAHash, NixHash}; use nix_compat::nixhash::{CAHash, NixHash};
use nix_compat::store_path::StorePath;
use tvix_eval::generators::Gen; use tvix_eval::generators::Gen;
use tvix_eval::{generators::GenCo, ErrorKind, Value}; use tvix_eval::{generators::GenCo, ErrorKind, Value};
use tvix_eval::{NixContextElement, NixString}; use tvix_eval::{NixContextElement, NixString};
@ -280,6 +282,44 @@ mod import_builtins {
.into(), .into(),
) )
} }
#[builtin("storePath")]
async fn builtin_store_path(
state: Rc<TvixStoreIO>,
co: GenCo,
path: Value,
) -> Result<Value, ErrorKind> {
let p = std::str::from_utf8(match &path {
Value::String(s) => s.as_bytes(),
Value::Path(p) => p.as_os_str().as_bytes(),
_ => {
return Err(ErrorKind::TypeError {
expected: "string or path",
actual: path.type_of(),
})
}
})?;
let path_exists = if let Ok((store_path, sub_path)) = StorePath::from_absolute_path_full(p)
{
if !sub_path.as_os_str().is_empty() {
false
} else {
state.store_path_exists(store_path.as_ref()).await?
}
} else {
false
};
if !path_exists {
return Err(ImportError::PathNotInStore(p.into()).into());
}
Ok(Value::String(NixString::new_context_from(
[NixContextElement::Plain(p.into())].into(),
p,
)))
}
} }
pub use import_builtins::builtins as import_builtins; pub use import_builtins::builtins as import_builtins;

View file

View file

@ -0,0 +1 @@
{ contextMatches = true; hasContext = true; }

View file

@ -0,0 +1,9 @@
let
path = builtins.unsafeDiscardStringContext "${../empty-file}";
storePath = builtins.storePath path;
context = builtins.getContext storePath;
in
{
hasContext = builtins.hasContext storePath;
contextMatches = context == { "${path}" = { path = true; }; };
}

View file

@ -421,7 +421,7 @@ impl EvalIO for TvixStoreIO {
{ {
if self if self
.tokio_handle .tokio_handle
.block_on(async { self.store_path_to_node(&store_path, &sub_path).await })? .block_on(self.store_path_to_node(&store_path, &sub_path))?
.is_some() .is_some()
{ {
Ok(true) Ok(true)