From 32ac7d6c6d06b5191fcc828b762b247b0e27dfda Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Mon, 10 Oct 2022 11:43:51 -0700 Subject: refactor(tvix/eval) s/NixPath/NixSearchPath/ Since NixString is the Rust type for nix strings, people might mistake NixPath for the Rust type of nix paths, which it is not. Let's call it NixSearchPath instead. Signed-off-by: Adam Joseph Change-Id: Ib2ea155c4b27cb90d6180a04ea7b951d86607373 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6927 Reviewed-by: kanepyork Tested-by: BuildkiteCI Reviewed-by: tazjin --- tvix/eval/src/eval.rs | 8 +- tvix/eval/src/lib.rs | 2 +- tvix/eval/src/nix_path.rs | 207 --------------------------------------- tvix/eval/src/nix_search_path.rs | 207 +++++++++++++++++++++++++++++++++++++++ tvix/eval/src/opcode.rs | 4 +- tvix/eval/src/vm.rs | 17 ++-- 6 files changed, 224 insertions(+), 221 deletions(-) delete mode 100644 tvix/eval/src/nix_path.rs create mode 100644 tvix/eval/src/nix_search_path.rs (limited to 'tvix/eval') diff --git a/tvix/eval/src/eval.rs b/tvix/eval/src/eval.rs index 809c8729f711..8973c1a95c19 100644 --- a/tvix/eval/src/eval.rs +++ b/tvix/eval/src/eval.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, path::PathBuf, rc::Rc}; use crate::{ builtins::global_builtins, errors::{Error, ErrorKind, EvalResult}, - nix_path::NixPath, + nix_search_path::NixSearchPath, observer::{DisassemblingObserver, NoOpObserver, TracingObserver}, value::Value, SourceCode, @@ -27,7 +27,7 @@ pub struct Options { /// A colon-separated list of directories to use to resolve `<...>`-style paths #[cfg_attr(feature = "repl", clap(long, short = 'I', env = "NIX_PATH"))] - nix_path: Option, + nix_search_path: Option, } pub fn interpret(code: &str, location: Option, options: Options) -> EvalResult { @@ -111,13 +111,13 @@ pub fn interpret(code: &str, location: Option, options: Options) -> Eva let result = if options.trace_runtime { crate::vm::run_lambda( - options.nix_path.unwrap_or_default(), + options.nix_search_path.unwrap_or_default(), &mut TracingObserver::new(std::io::stderr()), result.lambda, ) } else { crate::vm::run_lambda( - options.nix_path.unwrap_or_default(), + options.nix_search_path.unwrap_or_default(), &mut NoOpObserver::default(), result.lambda, ) diff --git a/tvix/eval/src/lib.rs b/tvix/eval/src/lib.rs index 3c4fe26cd478..a7505eaf3acc 100644 --- a/tvix/eval/src/lib.rs +++ b/tvix/eval/src/lib.rs @@ -12,7 +12,7 @@ mod value; mod vm; mod warnings; -mod nix_path; +mod nix_search_path; #[cfg(test)] mod properties; #[cfg(test)] diff --git a/tvix/eval/src/nix_path.rs b/tvix/eval/src/nix_path.rs deleted file mode 100644 index 61e45c66d065..000000000000 --- a/tvix/eval/src/nix_path.rs +++ /dev/null @@ -1,207 +0,0 @@ -use std::convert::Infallible; -use std::io; -use std::path::{Path, PathBuf}; -use std::str::FromStr; - -use crate::errors::ErrorKind; - -#[derive(Debug, Clone, PartialEq, Eq)] -enum NixPathEntry { - /// Resolve subdirectories of this path within `<...>` brackets. This - /// corresponds to bare paths within the `NIX_PATH` environment variable - /// - /// For example, with `NixPathEntry::Path("/example")` and the following - /// directory structure: - /// - /// ```notrust - /// example - /// └── subdir - /// └── grandchild - /// ``` - /// - /// A Nix path literal `` would resolve to `/example/subdir`, and a - /// Nix path literal `` would resolve to - /// `/example/subdir/grandchild` - Path(PathBuf), - - /// Resolve paths starting with `prefix` as subdirectories of `path`. This - /// corresponds to `prefix=path` within the `NIX_PATH` environment variable. - /// - /// For example, with `NixPathEntry::Prefix { prefix: "prefix", path: - /// "/example" }` and the following directory structure: - /// - /// ```notrust - /// example - /// └── subdir - /// └── grandchild - /// ``` - /// - /// A Nix path literal `` would resolve to `/example/subdir`, - /// and a Nix path literal `` would resolve to - /// `/example/subdir/grandchild` - Prefix { prefix: PathBuf, path: PathBuf }, -} - -impl NixPathEntry { - fn resolve(&self, lookup_path: &Path) -> io::Result> { - let resolve_in = - |parent: &Path, lookup_path: &Path| match parent.join(lookup_path).canonicalize() { - Ok(path) => Ok(Some(path)), - Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None), - Err(e) => Err(e), - }; - - match self { - NixPathEntry::Path(p) => resolve_in(p, lookup_path), - NixPathEntry::Prefix { prefix, path } => { - if let Ok(child_path) = lookup_path.strip_prefix(prefix) { - resolve_in(path, child_path) - } else { - Ok(None) - } - } - } - } -} - -impl FromStr for NixPathEntry { - type Err = Infallible; - - fn from_str(s: &str) -> Result { - match s.split_once('=') { - Some((prefix, path)) => Ok(Self::Prefix { - prefix: prefix.into(), - path: path.into(), - }), - None => Ok(Self::Path(s.into())), - } - } -} - -/// Struct implementing the format and path resolution rules of the `NIX_PATH` -/// environment variable. -/// -/// This struct can be constructed by parsing a string using the [`FromStr`] -/// impl, or via [`str::parse`]. Nix `<...>` paths can then be resolved using -/// [`NixPath::resolve`]. -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct NixPath { - entries: Vec, -} - -impl NixPath { - /// Attempt to resolve the given `path` within this [`NixPath`] using the - /// path resolution rules for `<...>`-style paths - #[allow(dead_code)] // TODO(grfn) - pub fn resolve

(&self, path: P) -> Result - where - P: AsRef, - { - let path = path.as_ref(); - for entry in &self.entries { - if let Some(p) = entry.resolve(path)? { - return Ok(p); - } - } - Err(ErrorKind::PathResolution(format!( - "path '{}' was not found in the Nix search path", - path.display() - ))) - } -} - -impl FromStr for NixPath { - type Err = Infallible; - - fn from_str(s: &str) -> Result { - let entries = s - .split(':') - .map(|s| s.parse()) - .collect::, _>>()?; - Ok(NixPath { entries }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - mod parse { - use super::*; - - #[test] - fn bare_paths() { - assert_eq!( - NixPath::from_str("/foo/bar:/baz").unwrap(), - NixPath { - entries: vec![ - NixPathEntry::Path("/foo/bar".into()), - NixPathEntry::Path("/baz".into()) - ], - } - ); - } - - #[test] - fn mixed_prefix_and_paths() { - assert_eq!( - NixPath::from_str("nixpkgs=/my/nixpkgs:/etc/nixos").unwrap(), - NixPath { - entries: vec![ - NixPathEntry::Prefix { - prefix: "nixpkgs".into(), - path: "/my/nixpkgs".into() - }, - NixPathEntry::Path("/etc/nixos".into()) - ], - } - ); - } - } - - mod resolve { - use std::env::current_dir; - - use path_clean::PathClean; - - use super::*; - - #[test] - fn simple_dir() { - let nix_path = NixPath::from_str("./.").unwrap(); - let res = nix_path.resolve("src").unwrap(); - assert_eq!(res, current_dir().unwrap().join("src").clean()); - } - - #[test] - fn failed_resolution() { - let nix_path = NixPath::from_str("./.").unwrap(); - let err = nix_path.resolve("nope").unwrap_err(); - assert!( - matches!(err, ErrorKind::PathResolution(..)), - "err = {err:?}" - ); - } - - #[test] - fn second_in_path() { - let nix_path = NixPath::from_str("./.:/").unwrap(); - let res = nix_path.resolve("bin").unwrap(); - assert_eq!(res, Path::new("/bin")); - } - - #[test] - fn prefix() { - let nix_path = NixPath::from_str("/:tvix=.").unwrap(); - let res = nix_path.resolve("tvix/src").unwrap(); - assert_eq!(res, current_dir().unwrap().join("src").clean()); - } - - #[test] - fn matching_prefix() { - let nix_path = NixPath::from_str("/:tvix=.").unwrap(); - let res = nix_path.resolve("tvix").unwrap(); - assert_eq!(res, current_dir().unwrap().clean()); - } - } -} diff --git a/tvix/eval/src/nix_search_path.rs b/tvix/eval/src/nix_search_path.rs new file mode 100644 index 000000000000..82a2a950998b --- /dev/null +++ b/tvix/eval/src/nix_search_path.rs @@ -0,0 +1,207 @@ +use std::convert::Infallible; +use std::io; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use crate::errors::ErrorKind; + +#[derive(Debug, Clone, PartialEq, Eq)] +enum NixSearchPathEntry { + /// Resolve subdirectories of this path within `<...>` brackets. This + /// corresponds to bare paths within the `NIX_PATH` environment variable + /// + /// For example, with `NixSearchPathEntry::Path("/example")` and the following + /// directory structure: + /// + /// ```notrust + /// example + /// └── subdir + /// └── grandchild + /// ``` + /// + /// A Nix path literal `` would resolve to `/example/subdir`, and a + /// Nix path literal `` would resolve to + /// `/example/subdir/grandchild` + Path(PathBuf), + + /// Resolve paths starting with `prefix` as subdirectories of `path`. This + /// corresponds to `prefix=path` within the `NIX_PATH` environment variable. + /// + /// For example, with `NixSearchPathEntry::Prefix { prefix: "prefix", path: + /// "/example" }` and the following directory structure: + /// + /// ```notrust + /// example + /// └── subdir + /// └── grandchild + /// ``` + /// + /// A Nix path literal `` would resolve to `/example/subdir`, + /// and a Nix path literal `` would resolve to + /// `/example/subdir/grandchild` + Prefix { prefix: PathBuf, path: PathBuf }, +} + +impl NixSearchPathEntry { + fn resolve(&self, lookup_path: &Path) -> io::Result> { + let resolve_in = + |parent: &Path, lookup_path: &Path| match parent.join(lookup_path).canonicalize() { + Ok(path) => Ok(Some(path)), + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(e), + }; + + match self { + NixSearchPathEntry::Path(p) => resolve_in(p, lookup_path), + NixSearchPathEntry::Prefix { prefix, path } => { + if let Ok(child_path) = lookup_path.strip_prefix(prefix) { + resolve_in(path, child_path) + } else { + Ok(None) + } + } + } + } +} + +impl FromStr for NixSearchPathEntry { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + match s.split_once('=') { + Some((prefix, path)) => Ok(Self::Prefix { + prefix: prefix.into(), + path: path.into(), + }), + None => Ok(Self::Path(s.into())), + } + } +} + +/// Struct implementing the format and path resolution rules of the `NIX_PATH` +/// environment variable. +/// +/// This struct can be constructed by parsing a string using the [`FromStr`] +/// impl, or via [`str::parse`]. Nix `<...>` paths can then be resolved using +/// [`NixSearchPath::resolve`]. +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct NixSearchPath { + entries: Vec, +} + +impl NixSearchPath { + /// Attempt to resolve the given `path` within this [`NixSearchPath`] using the + /// path resolution rules for `<...>`-style paths + #[allow(dead_code)] // TODO(grfn) + pub fn resolve

(&self, path: P) -> Result + where + P: AsRef, + { + let path = path.as_ref(); + for entry in &self.entries { + if let Some(p) = entry.resolve(path)? { + return Ok(p); + } + } + Err(ErrorKind::PathResolution(format!( + "path '{}' was not found in the Nix search path", + path.display() + ))) + } +} + +impl FromStr for NixSearchPath { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + let entries = s + .split(':') + .map(|s| s.parse()) + .collect::, _>>()?; + Ok(NixSearchPath { entries }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + mod parse { + use super::*; + + #[test] + fn bare_paths() { + assert_eq!( + NixSearchPath::from_str("/foo/bar:/baz").unwrap(), + NixSearchPath { + entries: vec![ + NixSearchPathEntry::Path("/foo/bar".into()), + NixSearchPathEntry::Path("/baz".into()) + ], + } + ); + } + + #[test] + fn mixed_prefix_and_paths() { + assert_eq!( + NixSearchPath::from_str("nixpkgs=/my/nixpkgs:/etc/nixos").unwrap(), + NixSearchPath { + entries: vec![ + NixSearchPathEntry::Prefix { + prefix: "nixpkgs".into(), + path: "/my/nixpkgs".into() + }, + NixSearchPathEntry::Path("/etc/nixos".into()) + ], + } + ); + } + } + + mod resolve { + use std::env::current_dir; + + use path_clean::PathClean; + + use super::*; + + #[test] + fn simple_dir() { + let nix_search_path = NixSearchPath::from_str("./.").unwrap(); + let res = nix_search_path.resolve("src").unwrap(); + assert_eq!(res, current_dir().unwrap().join("src").clean()); + } + + #[test] + fn failed_resolution() { + let nix_search_path = NixSearchPath::from_str("./.").unwrap(); + let err = nix_search_path.resolve("nope").unwrap_err(); + assert!( + matches!(err, ErrorKind::PathResolution(..)), + "err = {err:?}" + ); + } + + #[test] + fn second_in_path() { + let nix_search_path = NixSearchPath::from_str("./.:/").unwrap(); + let res = nix_search_path.resolve("bin").unwrap(); + assert_eq!(res, Path::new("/bin")); + } + + #[test] + fn prefix() { + let nix_search_path = NixSearchPath::from_str("/:tvix=.").unwrap(); + let res = nix_search_path.resolve("tvix/src").unwrap(); + assert_eq!(res, current_dir().unwrap().join("src").clean()); + } + + #[test] + fn matching_prefix() { + let nix_search_path = NixSearchPath::from_str("/:tvix=.").unwrap(); + let res = nix_search_path.resolve("tvix").unwrap(); + assert_eq!(res, current_dir().unwrap().clean()); + } + } +} diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs index 96234ccd233c..c44494d57aa9 100644 --- a/tvix/eval/src/opcode.rs +++ b/tvix/eval/src/opcode.rs @@ -113,9 +113,9 @@ pub enum OpCode { OpCoerceToString, // Paths - /// Attempt to resolve the Value on the stack using the configured [`NixPath`][] + /// Attempt to resolve the Value on the stack using the configured [`NixSearchPath`][] /// - /// [`NixPath`]: crate::nix_path::NixPath + /// [`NixSearchPath`]: crate::nix_search_path::NixSearchPath OpFindFile, // Type assertion operators diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index ebf3de297b5a..c54c005b0041 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -8,7 +8,7 @@ use path_clean::PathClean; use crate::{ chunk::Chunk, errors::{Error, ErrorKind, EvalResult}, - nix_path::NixPath, + nix_search_path::NixSearchPath, observer::RuntimeObserver, opcode::{CodeIdx, Count, JumpOffset, OpCode, StackIdx, UpvalueIdx}, upvalues::{UpvalueCarrier, Upvalues}, @@ -50,7 +50,7 @@ pub struct VM<'o> { /// Runtime warnings collected during evaluation. warnings: Vec, - nix_path: NixPath, + nix_search_path: NixSearchPath, observer: &'o mut dyn RuntimeObserver, } @@ -142,9 +142,9 @@ macro_rules! cmp_op { } impl<'o> VM<'o> { - pub fn new(nix_path: NixPath, observer: &'o mut dyn RuntimeObserver) -> Self { + pub fn new(nix_search_path: NixSearchPath, observer: &'o mut dyn RuntimeObserver) -> Self { Self { - nix_path, + nix_search_path, observer, frames: vec![], stack: vec![], @@ -518,7 +518,10 @@ impl<'o> VM<'o> { OpCode::OpFindFile => { let path = self.pop().to_str().map_err(|e| self.error(e))?; - let resolved = self.nix_path.resolve(path).map_err(|e| self.error(e))?; + let resolved = self + .nix_search_path + .resolve(path) + .map_err(|e| self.error(e))?; self.push(resolved.into()); } @@ -887,11 +890,11 @@ fn unwrap_or_clone_rc(rc: Rc) -> T { } pub fn run_lambda( - nix_path: NixPath, + nix_search_path: NixSearchPath, observer: &mut dyn RuntimeObserver, lambda: Rc, ) -> EvalResult { - let mut vm = VM::new(nix_path, observer); + let mut vm = VM::new(nix_search_path, observer); vm.enter_frame(lambda, Upvalues::with_capacity(0), 0)?; let value = vm.pop(); vm.force_for_output(&value)?; -- cgit 1.4.1