diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 38ac5a676..6c070cfc5 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -1,8 +1,6 @@ use crate::line_editor::configure_ctrl_c; use nu_command::commands::default_context::create_default_context; -use nu_engine::{ - print::maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext, -}; +use nu_engine::{evaluation_context, run_block, script::run_script_standalone, EvaluationContext}; #[allow(unused_imports)] pub(crate) use nu_engine::script::{process_script, LineResult}; @@ -37,6 +35,8 @@ use std::path::PathBuf; pub struct Options { pub config: Option, + pub history: Option, + pub save_history: bool, pub stdin: bool, pub scripts: Vec, } @@ -51,10 +51,22 @@ impl Options { pub fn new() -> Self { Self { config: None, + history: None, + save_history: true, stdin: false, scripts: vec![], } } + + pub fn history(&self, block: impl FnOnce(&std::path::Path)) { + if !self.save_history { + return; + } + + if let Some(file) = &self.history { + block(&file) + } + } } pub struct NuScript { @@ -125,9 +137,9 @@ pub fn search_paths() -> Vec { search_paths } -pub async fn run_script_file(options: Options) -> Result<(), Box> { +pub async fn run_script_file(mut options: Options) -> Result<(), Box> { let mut context = create_default_context(false)?; - let mut syncer = create_environment_syncer(&context, &options); + let mut syncer = create_environment_syncer(&context, &mut options); let config = syncer.get_config(); context.configure(&config, |_, ctx| { @@ -136,7 +148,7 @@ pub async fn run_script_file(options: Options) -> Result<(), Box> { syncer.sync_path_vars(ctx); if let Err(reason) = syncer.autoenv(ctx) { - ctx.host.lock().print_err(reason, &Text::from("")); + ctx.with_host(|host| host.print_err(reason, &Text::from(""))); } let _ = register_plugins(ctx); @@ -155,27 +167,51 @@ pub async fn run_script_file(options: Options) -> Result<(), Box> { Ok(()) } -fn create_environment_syncer(context: &EvaluationContext, options: &Options) -> EnvironmentSyncer { - if let Some(config_file) = &options.config { - let location = Some(AnchorLocation::File( - config_file.to_string_lossy().to_string(), - )); - let tag = Tag::unknown().anchored(location); +fn create_environment_syncer( + context: &EvaluationContext, + options: &mut Options, +) -> EnvironmentSyncer { + let configuration = match &options.config { + Some(config_file) => { + let location = Some(AnchorLocation::File( + config_file.to_string_lossy().to_string(), + )); - context.scope.add_var( - "config-path", - UntaggedValue::filepath(PathBuf::from(&config_file)).into_value(tag), - ); + let tag = Tag::unknown().anchored(location); - EnvironmentSyncer::with_config(Box::new(NuConfig::with(Some(config_file.into())))) - } else { - EnvironmentSyncer::new() - } + context.scope.add_var( + "config-path", + UntaggedValue::filepath(PathBuf::from(&config_file)).into_value(tag), + ); + + NuConfig::with(Some(config_file.into())) + } + None => NuConfig::new(), + }; + + let history_path = configuration.history_path(); + options.history = Some(history_path.clone()); + + let location = Some(AnchorLocation::File( + history_path.to_string_lossy().to_string(), + )); + + let tag = Tag::unknown().anchored(location); + + context.scope.add_var( + "history-path", + UntaggedValue::filepath(history_path).into_value(tag), + ); + + EnvironmentSyncer::with_config(Box::new(configuration)) } #[cfg(feature = "rustyline-support")] -pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), Box> { - let mut syncer = create_environment_syncer(&context, &options); +pub async fn cli( + mut context: EvaluationContext, + mut options: Options, +) -> Result<(), Box> { + let mut syncer = create_environment_syncer(&context, &mut options); let configuration = syncer.get_config(); @@ -187,7 +223,7 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), syncer.sync_path_vars(ctx); if let Err(reason) = syncer.autoenv(ctx) { - ctx.host.lock().print_err(reason, &Text::from("")); + ctx.with_host(|host| host.print_err(reason, &Text::from(""))); } let _ = configure_ctrl_c(ctx); @@ -214,11 +250,9 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), // Give ourselves a scope to work in context.scope.enter_scope(); - let env = context.get_env(); - context.scope.add_env_to_base(env); - - let history_path = nu_engine::history_path(&configuration); - let _ = rl.load_history(&history_path); + options.history(|file| { + let _ = rl.load_history(&file); + }); let mut session_text = String::new(); let mut line_start: usize = 0; @@ -254,6 +288,7 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), let prompt_line = prompt.as_string()?; context.scope.enter_scope(); + let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope); prompt_block.set_redirect(ExternalRedirection::Stdout); @@ -263,8 +298,6 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch()) } else { - // let env = context.get_env(); - let run_result = run_block(&prompt_block, &context, InputStream::empty()).await; context.scope.exit_scope(); @@ -272,7 +305,10 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), Ok(result) => match result.collect_string(Tag::unknown()).await { Ok(string_result) => { let errors = context.get_errors(); - maybe_print_errors(&context, Text::from(prompt_line)); + evaluation_context::maybe_print_errors( + &context, + Text::from(prompt_line), + ); context.clear_errors(); if !errors.is_empty() { @@ -357,7 +393,7 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), } if let Err(reason) = syncer.autoenv(ctx) { - ctx.host.lock().print_err(reason, &Text::from("")); + ctx.with_host(|host| host.print_err(reason, &Text::from(""))); } let _ = configure_rustyline_editor(&mut rl, config); @@ -365,31 +401,33 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), match line { LineResult::Success(line) => { - rl.add_history_entry(&line); - let _ = rl.save_history(&history_path); - maybe_print_errors(&context, Text::from(session_text.clone())); + options.history(|file| { + rl.add_history_entry(&line); + let _ = rl.save_history(&file); + }); + + evaluation_context::maybe_print_errors(&context, Text::from(session_text.clone())); } LineResult::ClearHistory => { - rl.clear_history(); - let _ = rl.save_history(&history_path); + options.history(|file| { + rl.clear_history(); + let _ = rl.save_history(&file); + }); } - LineResult::Error(line, err) => { - rl.add_history_entry(&line); - let _ = rl.save_history(&history_path); + LineResult::Error(line, reason) => { + options.history(|file| { + rl.add_history_entry(&line); + let _ = rl.save_history(&file); + }); - context - .host - .lock() - .print_err(err, &Text::from(session_text.clone())); - - maybe_print_errors(&context, Text::from(session_text.clone())); + context.with_host(|host| host.print_err(reason, &Text::from(session_text.clone()))); } LineResult::CtrlC => { - let config_ctrlc_exit = config::config(Tag::unknown())? - .get("ctrlc_exit") + let config_ctrlc_exit = configuration + .var("ctrlc_exit") .map(|s| s.value.is_true()) .unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells @@ -398,7 +436,10 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), } if ctrlcbreak { - let _ = rl.save_history(&history_path); + options.history(|file| { + let _ = rl.save_history(&file); + }); + std::process::exit(0); } else { context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)")); @@ -422,7 +463,9 @@ pub async fn cli(mut context: EvaluationContext, options: Options) -> Result<(), } // we are ok if we can not save history - let _ = rl.save_history(&history_path); + options.history(|file| { + let _ = rl.save_history(&file); + }); Ok(()) } @@ -512,14 +555,14 @@ fn current_branch() -> String { #[cfg(test)] mod tests { - use nu_engine::basic_evaluation_context; + use nu_engine::EvaluationContext; #[quickcheck] fn quickcheck_parse(data: String) -> bool { let (tokens, err) = nu_parser::lex(&data, 0); let (lite_block, err2) = nu_parser::parse_block(tokens); if err.is_none() && err2.is_none() { - let context = basic_evaluation_context().unwrap(); + let context = EvaluationContext::basic().unwrap(); let _ = nu_parser::classify_block(&lite_block, &context.scope); } true diff --git a/crates/nu-cli/src/env/environment_syncer.rs b/crates/nu-cli/src/env/environment_syncer.rs index c51cb998d..3f0231422 100644 --- a/crates/nu-cli/src/env/environment_syncer.rs +++ b/crates/nu-cli/src/env/environment_syncer.rs @@ -163,8 +163,8 @@ mod tests { use super::EnvironmentSyncer; use indexmap::IndexMap; use nu_data::config::tests::FakeConfig; - use nu_engine::basic_evaluation_context; use nu_engine::Env; + use nu_engine::EvaluationContext; use nu_errors::ShellError; use nu_test_support::fs::Stub::FileWithContent; use nu_test_support::playground::Playground; @@ -179,7 +179,7 @@ mod tests { #[test] fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError> { - let mut ctx = basic_evaluation_context()?; + let mut ctx = EvaluationContext::basic()?; ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new()))); let mut expected = IndexMap::new(); @@ -282,7 +282,7 @@ mod tests { #[test] fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError> { - let mut ctx = basic_evaluation_context()?; + let mut ctx = EvaluationContext::basic()?; ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new()))); let mut expected = IndexMap::new(); @@ -381,7 +381,7 @@ mod tests { #[test] fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> { - let mut ctx = basic_evaluation_context()?; + let mut ctx = EvaluationContext::basic()?; ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new()))); let mut expected = IndexMap::new(); @@ -457,7 +457,7 @@ mod tests { #[test] fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file( ) -> Result<(), ShellError> { - let mut ctx = basic_evaluation_context()?; + let mut ctx = EvaluationContext::basic()?; ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new()))); let expected = std::env::join_paths(vec![ @@ -544,7 +544,7 @@ mod tests { #[test] fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end( ) -> Result<(), ShellError> { - let mut ctx = basic_evaluation_context()?; + let mut ctx = EvaluationContext::basic()?; ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new()))); let expected = std::env::join_paths(vec![ diff --git a/crates/nu-cli/src/shell/helper.rs b/crates/nu-cli/src/shell/helper.rs index 2c5691553..0ea2827ab 100644 --- a/crates/nu-cli/src/shell/helper.rs +++ b/crates/nu-cli/src/shell/helper.rs @@ -149,7 +149,6 @@ impl rustyline::Helper for Helper {} #[cfg(test)] mod tests { use super::*; - use nu_engine::basic_evaluation_context; use rustyline::completion::Completer; use rustyline::line_buffer::LineBuffer; @@ -163,7 +162,7 @@ mod tests { buffer.insert_str(0, text); buffer.set_pos(text.len() - 1); - let helper = Helper::new(basic_evaluation_context().unwrap(), None); + let helper = Helper::new(EvaluationContext::basic().unwrap(), None); helper.update(&mut buffer, "cd ".len(), &replacement); @@ -183,7 +182,7 @@ mod tests { buffer.insert_str(0, text); buffer.set_pos(text.len() - 30); - let helper = Helper::new(basic_evaluation_context().unwrap(), None); + let helper = Helper::new(EvaluationContext::basic().unwrap(), None); helper.update(&mut buffer, "cd ".len(), &replacement); diff --git a/crates/nu-command/src/commands/classified/external.rs b/crates/nu-command/src/commands/classified/external.rs index c6936901d..465db1487 100644 --- a/crates/nu-command/src/commands/classified/external.rs +++ b/crates/nu-command/src/commands/classified/external.rs @@ -509,7 +509,7 @@ mod tests { #[cfg(feature = "which")] use futures::executor::block_on; #[cfg(feature = "which")] - use nu_engine::basic_evaluation_context; + use nu_engine::EvaluationContext; #[cfg(feature = "which")] use nu_errors::ShellError; #[cfg(feature = "which")] @@ -534,7 +534,7 @@ mod tests { let input = InputStream::empty(); let mut ctx = - basic_evaluation_context().expect("There was a problem creating a basic context."); + EvaluationContext::basic().expect("There was a problem creating a basic context."); assert!( run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout) @@ -548,7 +548,7 @@ mod tests { // async fn failure_run() -> Result<(), ShellError> { // let cmd = ExternalBuilder::for_name("fail").build(); - // let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context."); + // let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context."); // let stream = run_external_command(cmd, &mut ctx, None, false) // .await? // .expect("There was a problem running the external command."); diff --git a/crates/nu-command/src/commands/config/command.rs b/crates/nu-command/src/commands/config/command.rs index 3a81372fa..196dc05a1 100644 --- a/crates/nu-command/src/commands/config/command.rs +++ b/crates/nu-command/src/commands/config/command.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use nu_engine::CommandArgs; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; +use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use nu_stream::OutputStream; pub struct Command; @@ -22,9 +22,16 @@ impl WholeStreamCommand for Command { } async fn run(&self, args: CommandArgs) -> Result { - let name_span = args.call_info.name_tag.clone(); - let name = args.call_info.name_tag; - let result = nu_data::config::read(name_span, &None)?; + let name = args.call_info.name_tag.clone(); + + let path = match args.scope.get_var("config-path") { + Some(Value { + value: UntaggedValue::Primitive(Primitive::FilePath(path)), + .. + }) => Some(path), + _ => nu_data::config::default_path().ok(), + }; + let result = nu_data::config::read(&name, &path)?; Ok(futures::stream::iter(vec![ReturnSuccess::value( UntaggedValue::Row(result.into()).into_value(name), diff --git a/crates/nu-command/src/commands/default_context.rs b/crates/nu-command/src/commands/default_context.rs index 06590f7e9..2c4facf5a 100644 --- a/crates/nu-command/src/commands/default_context.rs +++ b/crates/nu-command/src/commands/default_context.rs @@ -1,10 +1,9 @@ -use crate::prelude::*; -use nu_engine::basic_evaluation_context; use nu_engine::whole_stream_command; +use nu_engine::EvaluationContext; use std::error::Error; pub fn create_default_context(interactive: bool) -> Result> { - let context = basic_evaluation_context()?; + let context = EvaluationContext::basic()?; { use crate::commands::*; diff --git a/crates/nu-command/src/commands/history.rs b/crates/nu-command/src/commands/history.rs index 29bb1bb77..0df581c0c 100644 --- a/crates/nu-command/src/commands/history.rs +++ b/crates/nu-command/src/commands/history.rs @@ -1,6 +1,5 @@ use crate::prelude::*; -use nu_data::config::{Conf, NuConfig}; -use nu_engine::history_path; +use nu_data::config::{path::history as history_path, NuConfig}; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; @@ -34,7 +33,7 @@ impl WholeStreamCommand for History { } async fn history(args: CommandArgs) -> Result { - let config: Box = Box::new(NuConfig::new()); + let config = NuConfig::new(); let tag = args.call_info.name_tag.clone(); let (Arguments { clear }, _) = args.process().await?; diff --git a/crates/nu-command/src/examples.rs b/crates/nu-command/src/examples.rs index 53b82c341..0d59e2c09 100644 --- a/crates/nu-command/src/examples.rs +++ b/crates/nu-command/src/examples.rs @@ -6,14 +6,12 @@ mod stub_generate; use double_echo::Command as DoubleEcho; use double_ls::Command as DoubleLs; -use stub_generate::{mock_path, Command as StubOpen}; - -use nu_engine::basic_evaluation_context; use nu_errors::ShellError; use nu_parser::ParserScope; use nu_protocol::hir::ClassifiedBlock; use nu_protocol::{ShellTypeName, Value}; use nu_source::AnchorLocation; +use stub_generate::{mock_path, Command as StubOpen}; use crate::commands::{ Append, BuildString, Each, Echo, First, Get, Keep, Last, Let, Nth, Select, StrCollect, Wrap, @@ -26,7 +24,7 @@ use futures::executor::block_on; pub fn test_examples(cmd: Command) -> Result<(), ShellError> { let examples = cmd.examples(); - let base_context = basic_evaluation_context()?; + let base_context = EvaluationContext::basic()?; base_context.add_commands(vec![ // Command Doubles @@ -92,7 +90,7 @@ pub fn test_examples(cmd: Command) -> Result<(), ShellError> { pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> { let examples = cmd.examples(); - let base_context = basic_evaluation_context()?; + let base_context = EvaluationContext::basic()?; base_context.add_commands(vec![ whole_stream_command(Echo {}), @@ -149,7 +147,7 @@ pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> { pub fn test_anchors(cmd: Command) -> Result<(), ShellError> { let examples = cmd.examples(); - let base_context = basic_evaluation_context()?; + let base_context = EvaluationContext::basic()?; base_context.add_commands(vec![ // Minimal restricted commands to aid in testing diff --git a/crates/nu-data/src/config.rs b/crates/nu-data/src/config.rs index 6e713febc..8057b5c1e 100644 --- a/crates/nu-data/src/config.rs +++ b/crates/nu-data/src/config.rs @@ -1,5 +1,6 @@ mod conf; mod nuconfig; +pub mod path; pub mod tests; @@ -185,7 +186,7 @@ pub fn default_path_for(file: &Option) -> Result { let file: &Path = file .as_ref() .map(AsRef::as_ref) - .unwrap_or_else(|| "config.toml".as_ref()); + .unwrap_or_else(|| self::path::DEFAULT_CONFIG_LOCATION.as_ref()); filename.push(file); Ok(filename) diff --git a/crates/nu-data/src/config/conf.rs b/crates/nu-data/src/config/conf.rs index c1beb18b8..3496c4e36 100644 --- a/crates/nu-data/src/config/conf.rs +++ b/crates/nu-data/src/config/conf.rs @@ -1,7 +1,9 @@ use nu_protocol::Value; +use std::any::Any; use std::fmt::Debug; pub trait Conf: Debug + Send { + fn as_any(&self) -> &dyn Any; fn is_modified(&self) -> Result>; fn var(&self, key: &str) -> Option; fn env(&self) -> Option; @@ -11,6 +13,10 @@ pub trait Conf: Debug + Send { } impl Conf for Box { + fn as_any(&self) -> &dyn Any { + self + } + fn is_modified(&self) -> Result> { (**self).is_modified() } diff --git a/crates/nu-data/src/config/nuconfig.rs b/crates/nu-data/src/config/nuconfig.rs index a052f4d1e..c8048c82d 100644 --- a/crates/nu-data/src/config/nuconfig.rs +++ b/crates/nu-data/src/config/nuconfig.rs @@ -2,15 +2,22 @@ use crate::config::{last_modified, read, Conf, Status}; use indexmap::IndexMap; use nu_protocol::Value; use nu_source::Tag; +use std::any::Any; use std::fmt::Debug; +use std::path::PathBuf; #[derive(Debug, Clone, Default)] pub struct NuConfig { + pub source_file: Option, pub vars: IndexMap, pub modified_at: Status, } impl Conf for NuConfig { + fn as_any(&self) -> &dyn Any { + self + } + fn is_modified(&self) -> Result> { self.is_modified() } @@ -30,7 +37,7 @@ impl Conf for NuConfig { fn reload(&mut self) { let vars = &mut self.vars; - if let Ok(variables) = read(Tag::unknown(), &None) { + if let Ok(variables) = read(Tag::unknown(), &self.source_file) { vars.extend(variables); self.modified_at = if let Ok(status) = last_modified(&None) { @@ -60,6 +67,7 @@ impl NuConfig { }; NuConfig { + source_file: source_file.clone(), vars, modified_at: NuConfig::get_last_modified(&source_file), } @@ -75,11 +83,16 @@ impl NuConfig { }; NuConfig { + source_file: None, vars, modified_at: NuConfig::get_last_modified(&None), } } + pub fn history_path(&self) -> PathBuf { + super::path::history(self) + } + pub fn get_last_modified(config_file: &Option) -> Status { if let Ok(status) = last_modified(config_file) { status @@ -91,15 +104,17 @@ impl NuConfig { pub fn is_modified(&self) -> Result> { let modified_at = &self.modified_at; - Ok(match (NuConfig::get_last_modified(&None), modified_at) { - (Status::LastModified(left), Status::LastModified(right)) => { - let left = left.duration_since(std::time::UNIX_EPOCH)?; - let right = (*right).duration_since(std::time::UNIX_EPOCH)?; + Ok( + match (NuConfig::get_last_modified(&self.source_file), modified_at) { + (Status::LastModified(left), Status::LastModified(right)) => { + let left = left.duration_since(std::time::UNIX_EPOCH)?; + let right = (*right).duration_since(std::time::UNIX_EPOCH)?; - left != right - } - (_, _) => false, - }) + left != right + } + (_, _) => false, + }, + ) } pub fn var(&self, key: &str) -> Option { diff --git a/crates/nu-data/src/config/path.rs b/crates/nu-data/src/config/path.rs new file mode 100644 index 000000000..7781787c1 --- /dev/null +++ b/crates/nu-data/src/config/path.rs @@ -0,0 +1,32 @@ +use crate::config::NuConfig; +use std::path::PathBuf; + +pub const DEFAULT_CONFIG_LOCATION: &str = "config.toml"; +const DEFAULT_HISTORY_LOCATION: &str = "history.txt"; + +pub fn history(config: &NuConfig) -> PathBuf { + let default_path = crate::config::user_data() + .map(|mut p| { + p.push(DEFAULT_HISTORY_LOCATION); + p + }) + .unwrap_or_else(|_| PathBuf::from(DEFAULT_HISTORY_LOCATION)); + + let path = &config.var("history-path"); + + path.as_ref().map_or(default_path.clone(), |custom_path| { + match custom_path.as_string() { + Ok(path) => PathBuf::from(path), + Err(_) => default_path, + } + }) +} + +pub fn source_file(config: &NuConfig) -> PathBuf { + match &config.source_file { + Some(path) => PathBuf::from(path), + None => { + crate::config::default_path().unwrap_or_else(|_| PathBuf::from(DEFAULT_CONFIG_LOCATION)) + } + } +} diff --git a/crates/nu-data/src/config/tests.rs b/crates/nu-data/src/config/tests.rs index 9a8935537..243025397 100644 --- a/crates/nu-data/src/config/tests.rs +++ b/crates/nu-data/src/config/tests.rs @@ -1,5 +1,6 @@ use crate::config::{Conf, NuConfig, Status}; use nu_protocol::Value; +use std::any::Any; use std::path::{Path, PathBuf}; #[derive(Debug, Clone)] @@ -9,6 +10,10 @@ pub struct FakeConfig { } impl Conf for FakeConfig { + fn as_any(&self) -> &dyn Any { + self + } + fn is_modified(&self) -> Result> { self.is_modified() } diff --git a/crates/nu-engine/src/basic_evaluation_context.rs b/crates/nu-engine/src/basic_evaluation_context.rs deleted file mode 100644 index f646bb881..000000000 --- a/crates/nu-engine/src/basic_evaluation_context.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::basic_shell_manager; -use crate::env::basic_host::BasicHost; -use crate::EvaluationContext; -use crate::Scope; -use parking_lot::Mutex; -use std::error::Error; -use std::sync::atomic::AtomicBool; -use std::sync::Arc; - -pub fn basic_evaluation_context() -> Result> { - Ok(EvaluationContext { - scope: Scope::new(), - host: Arc::new(parking_lot::Mutex::new(Box::new(BasicHost))), - current_errors: Arc::new(Mutex::new(vec![])), - ctrl_c: Arc::new(AtomicBool::new(false)), - user_recently_used_autoenv_untrust: Arc::new(AtomicBool::new(false)), - shell_manager: basic_shell_manager::basic_shell_manager()?, - windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())), - }) -} diff --git a/crates/nu-engine/src/basic_shell_manager.rs b/crates/nu-engine/src/basic_shell_manager.rs deleted file mode 100644 index f6d2edb4e..000000000 --- a/crates/nu-engine/src/basic_shell_manager.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::filesystem::filesystem_shell::FilesystemShell; -use crate::shell::shell_manager::ShellManager; - -use parking_lot::Mutex; -use std::error::Error; -use std::sync::atomic::AtomicUsize; -use std::sync::Arc; - -pub fn basic_shell_manager() -> Result> { - Ok(ShellManager { - current_shell: Arc::new(AtomicUsize::new(0)), - shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic()?)])), - }) -} diff --git a/crates/nu-engine/src/env/basic_host.rs b/crates/nu-engine/src/env/basic_host.rs index e9cbb14a0..602cb43b2 100644 --- a/crates/nu-engine/src/env/basic_host.rs +++ b/crates/nu-engine/src/env/basic_host.rs @@ -7,6 +7,21 @@ use std::ffi::OsString; #[derive(Debug)] pub struct BasicHost; +pub fn print_err(err: ShellError, source: &Text) { + if let Some(diag) = err.into_diagnostic() { + let source = source.to_string(); + let mut files = codespan_reporting::files::SimpleFiles::new(); + files.add("shell", source); + + let writer = termcolor::StandardStream::stderr(termcolor::ColorChoice::Auto); + let config = codespan_reporting::term::Config::default(); + + let _ = std::panic::catch_unwind(move || { + let _ = codespan_reporting::term::emit(&mut writer.lock(), &config, &files, &diag); + }); + } +} + impl Host for BasicHost { fn stdout(&mut self, out: &str) { match out { @@ -23,18 +38,7 @@ impl Host for BasicHost { } fn print_err(&mut self, err: ShellError, source: &Text) { - if let Some(diag) = err.into_diagnostic() { - let source = source.to_string(); - let mut files = codespan_reporting::files::SimpleFiles::new(); - files.add("shell", source); - - let writer = termcolor::StandardStream::stderr(termcolor::ColorChoice::Auto); - let config = codespan_reporting::term::Config::default(); - - let _ = std::panic::catch_unwind(move || { - let _ = codespan_reporting::term::emit(&mut writer.lock(), &config, &files, &diag); - }); - } + print_err(err, source); } #[allow(unused_variables)] diff --git a/crates/nu-engine/src/evaluate/variables.rs b/crates/nu-engine/src/evaluate/variables.rs index 17a4e83cf..cbddb5681 100644 --- a/crates/nu-engine/src/evaluate/variables.rs +++ b/crates/nu-engine/src/evaluate/variables.rs @@ -1,13 +1,24 @@ use crate::evaluate::scope::Scope; -use crate::history_path::history_path; +use nu_data::config::NuConfig; use nu_errors::ShellError; use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value}; use nu_source::Tag; pub fn nu(scope: &Scope, tag: impl Into) -> Result { - let env = &scope.get_env_vars(); let tag = tag.into(); + let env = &scope.get_env_vars(); + + let config = if let Some(Value { + value: UntaggedValue::Primitive(Primitive::FilePath(path)), + .. + }) = scope.get_var("config-path") + { + NuConfig::with(Some(path).map(|p| p.into_os_string())) + } else { + NuConfig::new() + }; + let mut nu_dict = TaggedDictBuilder::new(&tag); let mut dict = TaggedDictBuilder::new(&tag); @@ -18,16 +29,10 @@ pub fn nu(scope: &Scope, tag: impl Into) -> Result { } nu_dict.insert_value("env", dict.into_value()); - let config_file = match scope.get_var("config-path") { - Some(Value { - value: UntaggedValue::Primitive(Primitive::FilePath(path)), - .. - }) => Some(path), - _ => None, - }; - - let config = nu_data::config::read(&tag, &config_file)?; - nu_dict.insert_value("config", UntaggedValue::row(config).into_value(&tag)); + nu_dict.insert_value( + "config", + UntaggedValue::row(config.vars.clone()).into_value(&tag), + ); let mut table = vec![]; for v in env.iter() { @@ -49,15 +54,9 @@ pub fn nu(scope: &Scope, tag: impl Into) -> Result { let temp = std::env::temp_dir(); nu_dict.insert_value("temp-dir", UntaggedValue::filepath(temp).into_value(&tag)); - let config = if let Some(path) = config_file { - path - } else { - nu_data::config::default_path()? - }; - nu_dict.insert_value( "config-path", - UntaggedValue::filepath(config).into_value(&tag), + UntaggedValue::filepath(nu_data::config::path::source_file(&config)).into_value(&tag), ); #[cfg(feature = "rustyline-support")] @@ -69,11 +68,9 @@ pub fn nu(scope: &Scope, tag: impl Into) -> Result { ); } - let config: Box = Box::new(nu_data::config::NuConfig::new()); - let history = history_path(&config); nu_dict.insert_value( "history-path", - UntaggedValue::filepath(history).into_value(&tag), + UntaggedValue::filepath(nu_data::config::path::history(&config)).into_value(&tag), ); Ok(nu_dict.into_value()) diff --git a/crates/nu-engine/src/evaluation_context.rs b/crates/nu-engine/src/evaluation_context.rs index 12f245471..ef1167d93 100644 --- a/crates/nu-engine/src/evaluation_context.rs +++ b/crates/nu-engine/src/evaluation_context.rs @@ -1,13 +1,13 @@ use crate::call_info::UnevaluatedCallInfo; use crate::command_args::CommandArgs; -use crate::env::host::Host; +use crate::env::{basic_host::BasicHost, host::Host}; use crate::evaluate::scope::Scope; use crate::shell::shell_manager::ShellManager; use crate::whole_stream_command::Command; use indexmap::IndexMap; use nu_errors::ShellError; use nu_protocol::hir; -use nu_source::Tag; +use nu_source::{Tag, Text}; use nu_stream::{InputStream, OutputStream}; use parking_lot::Mutex; use std::sync::atomic::AtomicBool; @@ -27,6 +27,18 @@ pub struct EvaluationContext { } impl EvaluationContext { + pub fn basic() -> Result { + Ok(EvaluationContext { + scope: Scope::new(), + host: Arc::new(parking_lot::Mutex::new(Box::new(BasicHost))), + current_errors: Arc::new(Mutex::new(vec![])), + ctrl_c: Arc::new(AtomicBool::new(false)), + user_recently_used_autoenv_untrust: Arc::new(AtomicBool::new(false)), + shell_manager: ShellManager::basic()?, + windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())), + }) + } + pub fn from_args(args: &CommandArgs) -> EvaluationContext { EvaluationContext { scope: args.scope.clone(), @@ -121,3 +133,18 @@ impl EvaluationContext { output } } + +pub fn maybe_print_errors(context: &EvaluationContext, source: Text) -> bool { + let errors = context.current_errors.clone(); + let mut errors = errors.lock(); + + if errors.len() > 0 { + let error = errors[0].clone(); + *errors = vec![]; + + context.host.lock().print_err(error, &source); + true + } else { + false + } +} diff --git a/crates/nu-engine/src/history_path.rs b/crates/nu-engine/src/history_path.rs deleted file mode 100644 index 2fc1529b8..000000000 --- a/crates/nu-engine/src/history_path.rs +++ /dev/null @@ -1,22 +0,0 @@ -use nu_data::config::Conf; -use std::path::PathBuf; - -const DEFAULT_LOCATION: &str = "history.txt"; - -pub fn history_path(config: &dyn Conf) -> PathBuf { - let default_path = nu_data::config::user_data() - .map(|mut p| { - p.push(DEFAULT_LOCATION); - p - }) - .unwrap_or_else(|_| PathBuf::from(DEFAULT_LOCATION)); - - config - .var("history-path") - .map_or(default_path.clone(), |custom_path| { - match custom_path.as_string() { - Ok(path) => PathBuf::from(path), - Err(_) => default_path, - } - }) -} diff --git a/crates/nu-engine/src/lib.rs b/crates/nu-engine/src/lib.rs index d06d75321..981602346 100644 --- a/crates/nu-engine/src/lib.rs +++ b/crates/nu-engine/src/lib.rs @@ -1,25 +1,19 @@ -pub mod basic_evaluation_context; -pub mod basic_shell_manager; mod call_info; mod command_args; pub mod deserializer; pub mod documentation; mod env; mod evaluate; -mod evaluation_context; +pub mod evaluation_context; mod example; pub mod filesystem; -mod history_path; mod maybe_text_codec; pub mod plugin; -pub mod print; mod runnable_context; pub mod script; pub mod shell; mod whole_stream_command; -pub use crate::basic_evaluation_context::basic_evaluation_context; -pub use crate::basic_shell_manager::basic_shell_manager; pub use crate::call_info::UnevaluatedCallInfo; pub use crate::command_args::{ CommandArgs, EvaluatedCommandArgs, EvaluatedWholeStreamCommandArgs, RawCommandArgs, @@ -36,7 +30,6 @@ pub use crate::example::Example; pub use crate::filesystem::dir_info::{DirBuilder, DirInfo, FileInfo}; pub use crate::filesystem::filesystem_shell::FilesystemShell; pub use crate::filesystem::path; -pub use crate::history_path::history_path; pub use crate::maybe_text_codec::{MaybeTextCodec, StringOrBinary}; pub use crate::runnable_context::RunnableContext; pub use crate::shell::help_shell::{command_dict, HelpShell}; diff --git a/crates/nu-engine/src/print.rs b/crates/nu-engine/src/print.rs deleted file mode 100644 index 87f36e93a..000000000 --- a/crates/nu-engine/src/print.rs +++ /dev/null @@ -1,18 +0,0 @@ -use nu_source::Text; - -use crate::EvaluationContext; - -pub fn maybe_print_errors(context: &EvaluationContext, source: Text) -> bool { - let errors = context.current_errors.clone(); - let mut errors = errors.lock(); - - if errors.len() > 0 { - let error = errors[0].clone(); - *errors = vec![]; - - context.host.lock().print_err(error, &source); - true - } else { - false - } -} diff --git a/crates/nu-engine/src/script.rs b/crates/nu-engine/src/script.rs index 2df71e8cf..10de802c6 100644 --- a/crates/nu-engine/src/script.rs +++ b/crates/nu-engine/src/script.rs @@ -1,5 +1,5 @@ +use crate::path::canonicalize; use crate::run_block; -use crate::{path::canonicalize, print::maybe_print_errors}; use crate::{MaybeTextCodec, StringOrBinary}; use futures::StreamExt; use futures_codec::FramedRead; @@ -11,7 +11,7 @@ use nu_protocol::hir::{ use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value}; use nu_stream::{InputStream, ToInputStream}; -use crate::EvaluationContext; +use crate::{evaluation_context, EvaluationContext}; use log::{debug, trace}; use nu_source::{Span, Tag, Text}; use std::iter::Iterator; @@ -244,19 +244,16 @@ pub async fn run_script_standalone( } }; - maybe_print_errors(&context, Text::from(line)); + evaluation_context::maybe_print_errors(&context, Text::from(line)); if error_code != 0 && exit_on_error { std::process::exit(error_code); } } LineResult::Error(line, err) => { - context - .host - .lock() - .print_err(err, &Text::from(line.clone())); + context.with_host(|host| host.print_err(err, &Text::from(line.clone()))); - maybe_print_errors(&context, Text::from(line)); + evaluation_context::maybe_print_errors(&context, Text::from(line)); if exit_on_error { std::process::exit(1); } diff --git a/crates/nu-engine/src/shell/shell_manager.rs b/crates/nu-engine/src/shell/shell_manager.rs index 0abd8f463..00c063cdb 100644 --- a/crates/nu-engine/src/shell/shell_manager.rs +++ b/crates/nu-engine/src/shell/shell_manager.rs @@ -1,10 +1,12 @@ use crate::command_args::EvaluatedWholeStreamCommandArgs; use crate::maybe_text_codec::StringOrBinary; -use crate::shell::Shell; use futures::Stream; use nu_stream::OutputStream; +use crate::filesystem::filesystem_shell::FilesystemShell; use crate::shell::shell_args::{CdArgs, CopyArgs, LsArgs, MkdirArgs, MvArgs, RemoveArgs}; +use crate::shell::Shell; + use encoding_rs::Encoding; use nu_errors::ShellError; use nu_source::{Span, Tag}; @@ -20,6 +22,13 @@ pub struct ShellManager { } impl ShellManager { + pub fn basic() -> Result { + Ok(ShellManager { + current_shell: Arc::new(AtomicUsize::new(0)), + shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic()?)])), + }) + } + pub fn insert_at_current(&self, shell: Box) { self.shells.lock().push(shell); self.current_shell diff --git a/crates/nu-test-support/src/macros.rs b/crates/nu-test-support/src/macros.rs index 0009da793..759a710f9 100644 --- a/crates/nu-test-support/src/macros.rs +++ b/crates/nu-test-support/src/macros.rs @@ -48,6 +48,9 @@ macro_rules! nu { let mut process = match Command::new($crate::fs::executable_path()) .env("PATH", paths_joined) .arg("--skip-plugins") + .arg("--no-history") + .arg("--config-file") + .arg($crate::fs::DisplayPath::display_path(&$crate::fs::fixtures().join("playground/config/default.toml"))) .stdout(Stdio::piped()) .stdin(Stdio::piped()) .stderr(Stdio::piped()) @@ -153,7 +156,7 @@ macro_rules! nu_with_plugins { pub fn read_std(std: &[u8]) -> String { let out = String::from_utf8_lossy(std); - let out = out.lines().skip(1).collect::>().join("\n"); + let out = out.lines().collect::>().join("\n"); let out = out.replace("\r\n", ""); out.replace("\n", "") } diff --git a/crates/nu-test-support/src/playground.rs b/crates/nu-test-support/src/playground.rs index 636a83a60..caeb4f26c 100644 --- a/crates/nu-test-support/src/playground.rs +++ b/crates/nu-test-support/src/playground.rs @@ -9,4 +9,4 @@ mod tests; pub use director::Director; pub use matchers::says; pub use nu_process::{Executable, NuProcess, NuResult, Outcome}; -pub use play::{Dirs, Playground}; +pub use play::{Dirs, EnvironmentVariable, Playground}; diff --git a/crates/nu-test-support/src/playground/director.rs b/crates/nu-test-support/src/playground/director.rs index 87757b75d..c0b091fd1 100644 --- a/crates/nu-test-support/src/playground/director.rs +++ b/crates/nu-test-support/src/playground/director.rs @@ -1,48 +1,66 @@ use super::nu_process::*; +use super::EnvironmentVariable; use std::ffi::OsString; use std::fmt; #[derive(Default, Debug)] pub struct Director { pub cwd: Option, + pub environment_vars: Vec, pub config: Option, - pub pipeline: Option, + pub pipeline: Option>, pub executable: Option, } impl Director { pub fn cococo(&self, arg: &str) -> Self { - let mut process = NuProcess::default(); + let mut process = NuProcess { + environment_vars: self.environment_vars.clone(), + ..Default::default() + }; + process.args(&["--testbin", "cococo", arg]); Director { config: self.config.clone(), executable: Some(process), + environment_vars: self.environment_vars.clone(), ..Default::default() } } + pub fn and_then(&mut self, commands: &str) -> &mut Self { + let commands = commands.to_string(); + + if let Some(ref mut pipeline) = self.pipeline { + pipeline.push(commands); + } else { + self.pipeline = Some(vec![commands]); + } + + self + } + pub fn pipeline(&self, commands: &str) -> Self { let mut director = Director { pipeline: if commands.is_empty() { None } else { - Some(format!( - " - {} - exit", - commands - )) + Some(vec![commands.to_string()]) }, ..Default::default() }; - let mut process = NuProcess::default(); + let mut process = NuProcess { + environment_vars: self.environment_vars.clone(), + ..Default::default() + }; if let Some(working_directory) = &self.cwd { process.cwd(working_directory); } process.arg("--skip-plugins"); + process.arg("--no-history"); if let Some(config_file) = self.config.as_ref() { process.args(&[ "--config-file", @@ -64,7 +82,7 @@ impl Director { } impl Executable for Director { - fn execute(&self) -> NuResult { + fn execute(&mut self) -> NuResult { use std::io::Write; use std::process::Stdio; @@ -81,13 +99,16 @@ impl Executable for Director { Err(why) => panic!("Can't run test {}", why.to_string()), }; - if let Some(pipeline) = &self.pipeline { - process - .stdin - .as_mut() - .expect("couldn't open stdin") - .write_all(pipeline.as_bytes()) - .expect("couldn't write to stdin"); + if let Some(pipelines) = &self.pipeline { + let child = process.stdin.as_mut().expect("Failed to open stdin"); + + for pipeline in pipelines.iter() { + child + .write_all(format!("{}\n", pipeline).as_bytes()) + .expect("Could not write to"); + } + + child.write_all(b"exit\n").expect("Could not write to"); } process diff --git a/crates/nu-test-support/src/playground/nu_process.rs b/crates/nu-test-support/src/playground/nu_process.rs index fcbd6078d..450456216 100644 --- a/crates/nu-test-support/src/playground/nu_process.rs +++ b/crates/nu-test-support/src/playground/nu_process.rs @@ -1,12 +1,12 @@ -use crate::fs::executable_path; -use std::collections::HashMap; +use super::EnvironmentVariable; +use crate::fs::{binaries as test_bins_path, executable_path}; use std::ffi::{OsStr, OsString}; use std::fmt; use std::path::Path; use std::process::{Command, ExitStatus}; pub trait Executable { - fn execute(&self) -> NuResult; + fn execute(&mut self) -> NuResult; } #[derive(Clone, Debug)] @@ -36,7 +36,7 @@ pub struct NuError { #[derive(Clone, Debug)] pub struct NuProcess { pub arguments: Vec, - pub environment_vars: HashMap>, + pub environment_vars: Vec, pub cwd: Option, } @@ -56,7 +56,7 @@ impl Default for NuProcess { fn default() -> Self { Self { arguments: vec![], - environment_vars: HashMap::default(), + environment_vars: Vec::default(), cwd: None, } } @@ -90,6 +90,21 @@ impl NuProcess { command.current_dir(cwd); } + command.env_clear(); + + let paths = vec![test_bins_path()]; + + let paths_joined = match std::env::join_paths(paths.iter()) { + Ok(all) => all, + Err(_) => panic!("Couldn't join paths for PATH var."), + }; + + command.env("PATH", paths_joined); + + for env_var in &self.environment_vars { + command.env(&env_var.name, &env_var.value); + } + for arg in &self.arguments { command.arg(arg); } diff --git a/crates/nu-test-support/src/playground/play.rs b/crates/nu-test-support/src/playground/play.rs index d198ff04f..209256ed7 100644 --- a/crates/nu-test-support/src/playground/play.rs +++ b/crates/nu-test-support/src/playground/play.rs @@ -7,11 +7,27 @@ use std::path::{Path, PathBuf}; use std::str; use tempfile::{tempdir, TempDir}; +#[derive(Default, Clone, Debug)] +pub struct EnvironmentVariable { + pub name: String, + pub value: String, +} + +impl EnvironmentVariable { + fn new(name: &str, value: &str) -> Self { + Self { + name: name.to_string(), + value: value.to_string(), + } + } +} + pub struct Playground<'a> { root: TempDir, tests: String, cwd: PathBuf, config: PathBuf, + environment_vars: Vec, dirs: &'a Dirs, } @@ -71,6 +87,7 @@ impl<'a> Playground<'a> { tests: topic.to_string(), cwd: nuplay_dir, config: fixtures.join("playground/config/default.toml"), + environment_vars: Vec::default(), dirs: &Dirs::default(), }; @@ -108,6 +125,12 @@ impl<'a> Playground<'a> { self } + pub fn with_env(&mut self, name: &str, value: &str) -> &mut Self { + self.environment_vars + .push(EnvironmentVariable::new(name, value)); + self + } + pub fn get_config(&self) -> &str { self.config.to_str().expect("could not convert path.") } @@ -116,6 +139,7 @@ impl<'a> Playground<'a> { Director { cwd: Some(self.dirs.test().into()), config: Some(self.config.clone().into()), + environment_vars: self.environment_vars.clone(), ..Default::default() } } diff --git a/src/main.rs b/src/main.rs index 06eb03524..877ae21cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,6 +64,13 @@ fn main() -> Result<(), Box> { .multiple(false) .takes_value(false), ) + .arg( + Arg::with_name("no-history") + .hidden(true) + .long("no-history") + .multiple(false) + .takes_value(false), + ) .arg( Arg::with_name("script") .help("the nu script to run") @@ -95,6 +102,7 @@ fn main() -> Result<(), Box> { .value_of("config-file") .map(std::ffi::OsString::from); options.stdin = matches.is_present("stdin"); + options.save_history = !matches.is_present("no-history"); let loglevel = match matches.value_of("loglevel") { None => LevelFilter::Warn, diff --git a/tests/shell/environment/in_sync.rs b/tests/shell/environment/in_sync.rs new file mode 100644 index 000000000..98503fcb8 --- /dev/null +++ b/tests/shell/environment/in_sync.rs @@ -0,0 +1,113 @@ +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::fs::{AbsolutePath, DisplayPath}; +use nu_test_support::playground::{says, Playground}; + +use std::path::PathBuf; + +use hamcrest2::assert_that; +use hamcrest2::prelude::*; + +#[test] +fn setting_environment_value_to_configuration_should_pick_up_into_in_memory_environment_on_runtime() +{ + Playground::setup("environment_syncing_test_1", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [env] + SHELL = "/local/nu" + "#, + )]); + + assert_that!( + nu.pipeline("config set env.USER NUNO; echo $nothing") + .and_then("echo $nu.env.USER"), + says().to_stdout("NUNO") + ); + }); +} + +#[test] +fn inherited_environment_values_not_present_in_configuration_should_pick_up_into_in_memory_environment( +) { + Playground::setup("environment_syncing_test_2", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [env] + SHELL = "/local/nu" + "#, + )]) + .with_config(&file) + .with_env("USER", "NUNO"); + + assert_that!(nu.pipeline("echo $nu.env.USER"), says().to_stdout("NUNO")); + }); +} + +#[test] +fn environment_values_present_in_configuration_overwrites_inherited_environment_values() { + Playground::setup("environment_syncing_test_3", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [env] + SHELL = "/usr/bin/you_already_made_the_nu_choice" + "#, + )]) + .with_config(&file) + .with_env("SHELL", "/usr/bin/sh"); + + assert_that!( + nu.pipeline("echo $nu.env.SHELL"), + says().to_stdout("/usr/bin/you_already_made_the_nu_choice") + ); + }); +} + +#[test] +fn inherited_environment_path_values_not_present_in_configuration_should_pick_up_into_in_memory_environment( +) { + Playground::setup("environment_syncing_test_4", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + let expected_paths = vec![ + PathBuf::from("/Users/andresrobalino/.volta/bin"), + PathBuf::from("/Users/mosqueteros/bin"), + PathBuf::from("/path/to/be/added"), + ] + .iter() + .map(|p| p.display_path()) + .collect::>() + .join("-"); + + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"] + "#, + )]) + .with_config(&file) + .with_env("PATH", &PathBuf::from("/path/to/be/added").display_path()); + + assert_that!( + nu.pipeline("echo $nu.path | str collect '-'"), + says().to_stdout(&expected_paths) + ); + }); +} diff --git a/tests/shell/environment/mod.rs b/tests/shell/environment/mod.rs index a2be916d9..b3a0b763f 100644 --- a/tests/shell/environment/mod.rs +++ b/tests/shell/environment/mod.rs @@ -1,4 +1,5 @@ mod configuration; +mod in_sync; mod nu_env; pub mod support {