From 80daaae806f14bf9eaa22818889dbbaa718bd1ff Mon Sep 17 00:00:00 2001 From: gpwclark Date: Thu, 8 Feb 2024 11:56:30 +0100 Subject: [PATCH] check load path --- Cargo.lock | 111 ++++++++++++++++++++++++++- slosh/Cargo.toml | 2 + slosh/src/load_eval.rs | 106 ++++++++++++++------------ slosh/src/main.rs | 167 +++++++++++++++++++++++++++++------------ 4 files changed, 286 insertions(+), 100 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2466bec11..183e7cf936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,12 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "glob" version = "0.3.1" @@ -276,7 +282,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.9", ] [[package]] @@ -288,11 +304,24 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + [[package]] name = "proc-macro2" version = "1.0.76" @@ -311,6 +340,43 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -320,6 +386,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "regex" version = "1.10.2" @@ -349,6 +424,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "ryu" version = "1.0.16" @@ -425,7 +509,7 @@ dependencies = [ "libc", "log", "numtoa", - "parking_lot", + "parking_lot 0.11.2", "scopeguard", "winapi", ] @@ -455,6 +539,8 @@ dependencies = [ "sl-compiler", "sl-liner", "slvm", + "temp-env", + "tempdir", "unicode-width", "unicode_reader", ] @@ -500,6 +586,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "temp-env" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050" +dependencies = [ + "parking_lot 0.12.1", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand", + "remove_dir_all", +] + [[package]] name = "termcolor" version = "1.4.1" diff --git a/slosh/Cargo.toml b/slosh/Cargo.toml index 117f854bb5..0dc039f314 100644 --- a/slosh/Cargo.toml +++ b/slosh/Cargo.toml @@ -19,6 +19,8 @@ glob = "0.3" [dev-dependencies] regex = "1.10.2" lazy_static = "1.4.0" +tempdir = "0.3.7" +temp-env = "0.3.6" [build-dependencies] chrono = "0.4.7" diff --git a/slosh/src/load_eval.rs b/slosh/src/load_eval.rs index d0ee095a33..da28e52f94 100644 --- a/slosh/src/load_eval.rs +++ b/slosh/src/load_eval.rs @@ -63,55 +63,7 @@ pub(crate) fn load_internal(vm: &mut SloshVm, name: &'static str) -> VMResult(name.as_ref()).is_ok() { Ok(Cow::Borrowed(name)) } else { - let i_g = vm.intern("*load-path*"); - if let Some(g) = vm.global_intern_slot(i_g) { - if let Value::Vector(h) = vm.get_global(g) { - let paths = vm.get_vector(h); - let mut found = None; - for path in paths { - match path { - Value::StringConst(i) => { - let mut p = PathBuf::new(); - p.push(vm.get_interned(*i)); - p.push(name); - if p.exists() { - if let Ok(p) = p.into_os_string().into_string() { - found = Some(p.into()); - break; - } - } - } - Value::String(h) => { - let mut p = PathBuf::new(); - p.push(vm.get_string(*h)); - p.push(name); - if p.exists() { - if let Ok(p) = p.into_os_string().into_string() { - found = Some(p.into()); - break; - } - } - } - _ => {} - } - } - if let Some(p) = found { - Ok(p) - } else { - Err(VMError::new( - "io", - format!("{name}: not found on *load-path*!"), - )) - } - } else { - Err(VMError::new( - "io", - format!("{name}: *load-path* not a vector!"), - )) - } - } else { - Err(VMError::new("io", format!("{name}: *load-path* not set!"))) - } + find_file_in_load_path(vm, name) }; let mut reader = match fname { Ok(fname) => match std::fs::File::open(&*fname) { @@ -152,6 +104,62 @@ pub(crate) fn load_internal(vm: &mut SloshVm, name: &'static str) -> VMResult(vm: &'a mut SloshVm, name: &'b str) -> VMResult> { + let i_g = vm.intern("*load-path*"); + if let Some(g) = vm.global_intern_slot(i_g) { + if let Value::Vector(h) = vm.get_global(g) { + let paths = vm.get_vector(h); + let mut found = None; + for path in paths { + match path { + Value::StringConst(i) => { + let mut p = PathBuf::new(); + p.push(vm.get_interned(*i)); + p.push(name); + if p.exists() && !p.is_dir() { + if let Ok(p) = p.into_os_string().into_string() { + found = Some(p.into()); + break; + } + } + } + Value::String(h) => { + let mut p = PathBuf::new(); + p.push(vm.get_string(*h)); + p.push(name); + if p.exists() && !p.is_dir() { + if let Ok(p) = p.into_os_string().into_string() { + found = Some(p.into()); + break; + } + } + } + _ => {} + } + } + if let Some(p) = found { + Ok(p) + } else { + Err(VMError::new( + "io", + format!("{name}: not found on *load-path*!"), + )) + } + } else { + Err(VMError::new( + "io", + format!("{name}: *load-path* not a vector!"), + )) + } + } else { + Err(VMError::new("io", format!("{name}: *load-path* not set!"))) + } +} + fn load(vm: &mut SloshVm, registers: &[Value]) -> VMResult { if registers.len() != 1 { return Err(VMError::new_compile( diff --git a/slosh/src/main.rs b/slosh/src/main.rs index 35ccbcdfcc..f481a6c73d 100644 --- a/slosh/src/main.rs +++ b/slosh/src/main.rs @@ -1,12 +1,13 @@ extern crate sl_liner; use std::cell::RefCell; -use std::env; use std::ffi::OsString; -use std::fs::create_dir_all; +use std::fs::{create_dir_all, File}; use std::io::{BufRead, ErrorKind, Write}; -use std::path::PathBuf; +use std::ops::DerefMut; +use std::path::Path; use std::sync::Arc; +use std::{env, fs}; use slvm::opcodes::*; @@ -92,26 +93,27 @@ fn get_prompt(env: &mut SloshVm) -> String { } } -fn load_sloshrc() { - if let Ok(mut rcfile) = env::var("HOME") { - if rcfile.ends_with('/') { - rcfile.push_str(".config/slosh"); - } else { - rcfile.push_str("/.config/slosh"); - } - - ENV.with(|renv| { - let mut env = renv.borrow_mut(); - let i_path = env.intern(&rcfile); - let v = vec![Value::StringConst(i_path)]; - let path = env.alloc_vector(v); - add_global_value( - &mut env, - "*load-path*", - path, - "Usage: (set '*load-path* '(\"/path/one\" \"/path/two\")) +/// Given a [`SloshVm`] and a String, usually the rc file for slosh, set +/// the *load-path* global variable to facilitate proper loading of scripts. +/// +/// It is assumed users will put slosh scripts at the location(s) dictated by +/// *load-path*. +fn set_initial_load_path(env: &mut SloshVm, load_paths: Vec) { + let mut v = vec![]; + for path in load_paths { + let i_path = env.intern(&path); + v.push(Value::StringConst(i_path)); + } + let path = env.alloc_vector(v); + add_global_value( + env, + "*load-path*", + path, + "Usage: (set '*load-path* '(\"/path/one\" \"/path/two\")) Set the a list of paths to search for loading scripts with the load form. +Paths are a vector and are searched in index order for the file name of +the path to be loaded. Section: scripting @@ -120,32 +122,51 @@ Example: ;(load \"script-in-path\") t ", - ); - let rcpath: PathBuf = rcfile.clone().into(); - if !rcpath.exists() { - match create_dir_all(&rcpath) { - Ok(_) => {} - Err(e) => eprintln!( - "error creating default config directory {}: {e}", - rcpath.to_string_lossy() - ), - } + ); +} + +/// Expected that the default rcfile will be in the user's home directory +/// at `$HOME/.config/slosh/` otherwise the directory structure will be created. +fn load_sloshrc() { + if let Ok(mut rcfile) = env::var("HOME") { + let path_suffix = if let Ok(x) = fs::metadata::<&Path>(rcfile.as_ref()) { + if x.is_dir() { + ".config/slosh" + } else { + "/.config/slosh" + } + } else { + // This means provided $HOME dir doesn't exist + // (PathBuf::exists(...) just verifies if fs::metadata::<&Path>(...).is_ok()). + // If there is no HOME dir try to create it to guarantee that there is a $HOME and + // that it is a directory. + match create_dir_all::<&Path>(rcfile.as_ref()) { + Ok(_) => {} + Err(e) => eprintln!( + "environment variable HOME did not point to valid directory nor could that directory be created.{}: {e}", + rcfile + ), } + ".config/slosh" + }; + rcfile.push_str(path_suffix); + if fs::metadata::<&Path>(rcfile.as_ref()).is_ok() { + match create_dir_all::<&Path>(rcfile.as_ref()) { + Ok(_) => {} + Err(e) => eprintln!("error creating default config directory {}: {e}", rcfile), + } + } + ENV.with(|renv| { + let mut env = renv.borrow_mut(); + set_initial_load_path(env.deref_mut(), vec![rcfile.clone()]); rcfile.push_str("/init.slosh"); - let rcpath: PathBuf = rcfile.clone().into(); - if !rcpath.exists() { - match std::fs::File::create(&rcpath) { + if fs::metadata::<&Path>(rcfile.as_ref()).is_err() { + match File::create::<&Path>(rcfile.as_ref()) { Ok(mut f) => match f.write_all(SLSHRC.as_bytes()) { Ok(_) => {} - Err(e) => eprintln!( - "error writing default config {}: {e}", - rcpath.to_string_lossy() - ), + Err(e) => eprintln!("error writing default config {}: {e}", rcfile), }, - Err(e) => eprintln!( - "error creating default config {}: {e}", - rcpath.to_string_lossy() - ), + Err(e) => eprintln!("error creating default config {}: {e}", rcfile), } } let script = env.intern(&rcfile); @@ -483,7 +504,7 @@ fn exec_expression(res: String, env: &mut SloshVm) { } #[cfg(test)] -mod tests { +mod doc_tests { use super::*; use crate::tests::utils::exec; use compile_state::state::{new_slosh_vm, CompileState, SloshVm, SloshVmTrait}; @@ -755,26 +776,26 @@ mod tests { impl Display for DocError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let str = match self { - DocError::NoDocString{ symbol} => { + DocError::NoDocString { symbol } => { format!( "Either documentation provided does not conform to conventional layout or no documentation string provided for symbol {symbol} all slosh functions written in Rust must have a valid documentation string." ) } - DocError::ExemptFromProperDocString{ symbol} => { + DocError::ExemptFromProperDocString { symbol } => { format!( "No documentation needed for provided symbol {symbol}." ) } - DocError::DocStringMissingSection{ symbol, section} => { + DocError::DocStringMissingSection { symbol, section } => { format!("Invalid documentation string for symbol {symbol}, missing required section {section:?}") } - DocError::DocStringMustStartWithUsage{ symbol } => { + DocError::DocStringMustStartWithUsage { symbol } => { format!( "Invalid documentation string for symbol {symbol}, first line must start with \"Usage:\"" ) } } - .to_string(); + .to_string(); write!(f, "{}", str) } } @@ -808,3 +829,53 @@ mod tests { } } } + +#[cfg(test)] +mod path_tests { + extern crate tempdir; + + use crate::{load_sloshrc, set_initial_load_path, ENV}; + use std::fs::{create_dir_all, File}; + use std::io::Write; + use std::ops::DerefMut; + use temp_env; + use tempdir::TempDir; + + #[test] + fn test_load_path() { + // create home dir + let tmp_dir = TempDir::new("test_load_path").unwrap(); + let home_dir = tmp_dir.path().to_str(); + let home_path = home_dir.unwrap().to_string(); + + // create a dir with an add fcn that adds 1 in add.slosh + let tmp_0 = tmp_dir.path().join("tmp_0"); + create_dir_all(tmp_0.clone()).unwrap(); + let file_0 = tmp_0.as_path().join("add.slosh"); + let mut file_0 = File::create(file_0).unwrap(); + writeln!(file_0, "(def add (fn (x) (+ 1 x)").unwrap(); + + // create a dir with an add fcn that adds 2 in add.slosh + let tmp_1 = tmp_dir.path().join("tmp_1"); + create_dir_all(tmp_1.clone()).unwrap(); + let file_1 = tmp_1.as_path().join("add.slosh"); + let mut file_1 = File::create(file_1).unwrap(); + writeln!(file_1, "(def add (fn (x) (+ 2 x)").unwrap(); + + temp_env::with_var("HOME", home_dir, || { + // Run some code where `MY_ENV_VAR` set to `"production"`. + load_sloshrc(); + ENV.with(|env| { + let mut vm = env.borrow_mut(); + set_initial_load_path( + vm.deref_mut(), + vec![ + home_path, + tmp_0.to_str().unwrap().to_string(), + tmp_1.to_str().unwrap().to_string(), + ], + ); + }); + }); + } +}