account for startup commands in the scope. (#3261)

* Revert "Impl one configurable function to run scripts (#3242)"
* pass config startup.
This commit is contained in:
Andrés N. Robalino 2021-04-04 00:14:58 -05:00 committed by GitHub
parent 4c09716ad8
commit 00acf22f5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 220 additions and 306 deletions

View file

@ -1,10 +1,6 @@
use crate::line_editor::configure_ctrl_c; use crate::line_editor::configure_ctrl_c;
use nu_command::commands::default_context::create_default_context; use nu_command::commands::default_context::create_default_context;
use nu_engine::{ use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
filesystem::filesystem_shell::FilesystemShellMode, maybe_print_errors, run_block, script,
EvaluationContext,
};
use std::error::Error;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use nu_engine::script::{process_script, LineResult}; pub(crate) use nu_engine::script::{process_script, LineResult};
@ -19,7 +15,7 @@ use crate::line_editor::{
use nu_data::config; use nu_data::config;
use nu_source::{Tag, Text}; use nu_source::{Tag, Text};
use nu_stream::InputStream; use nu_stream::InputStream;
use std::ffi::OsString; use std::ffi::{OsStr, OsString};
#[allow(unused_imports)] #[allow(unused_imports)]
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@ -28,11 +24,10 @@ use rustyline::{self, error::ReadlineError};
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_parser::ParserScope; use nu_parser::ParserScope;
use nu_protocol::{ use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
hir::ExternalRedirection, ConfigPath, NuScript, RunScriptOptions, UntaggedValue, Value,
};
use log::trace; use log::trace;
use std::error::Error;
use std::iter::Iterator; use std::iter::Iterator;
use std::path::PathBuf; use std::path::PathBuf;
@ -60,6 +55,45 @@ impl Options {
} }
} }
pub struct NuScript {
pub filepath: Option<OsString>,
pub contents: String,
}
impl NuScript {
pub fn code<'a>(content: impl Iterator<Item = &'a str>) -> Result<Self, ShellError> {
let text = content
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n");
Ok(Self {
filepath: None,
contents: text,
})
}
pub fn get_code(&self) -> &str {
&self.contents
}
pub fn source_file(path: &OsStr) -> Result<Self, ShellError> {
use std::fs::File;
use std::io::Read;
let path = path.to_os_string();
let mut file = File::open(&path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
Ok(Self {
filepath: Some(path),
contents: buffer,
})
}
}
pub fn search_paths() -> Vec<std::path::PathBuf> { pub fn search_paths() -> Vec<std::path::PathBuf> {
use std::env; use std::env;
@ -89,11 +123,8 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
search_paths search_paths
} }
pub async fn run_script_file( pub async fn run_script_file(options: Options) -> Result<(), Box<dyn Error>> {
options: Options, let context = create_default_context(false)?;
run_options: RunScriptOptions,
) -> Result<(), Box<dyn Error>> {
let context = create_default_context(FilesystemShellMode::Script, false)?;
if let Some(cfg) = options.config { if let Some(cfg) = options.config {
load_cfg_as_global_cfg(&context, PathBuf::from(cfg)).await; load_cfg_as_global_cfg(&context, PathBuf::from(cfg)).await;
@ -104,9 +135,12 @@ pub async fn run_script_file(
let _ = register_plugins(&context); let _ = register_plugins(&context);
let _ = configure_ctrl_c(&context); let _ = configure_ctrl_c(&context);
for script in options.scripts { let script = options
script::run_script(script, &run_options, &context).await; .scripts
} .get(0)
.ok_or_else(|| ShellError::unexpected("Nu source code not available"))?;
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true).await?;
Ok(()) Ok(())
} }
@ -184,10 +218,6 @@ pub async fn cli(context: EvaluationContext, options: Options) -> Result<(), Box
let mut ctrlcbreak = false; let mut ctrlcbreak = false;
let mut run_options = RunScriptOptions::default()
.cli_mode(true)
.redirect_stdin(false);
loop { loop {
if context.ctrl_c.load(Ordering::SeqCst) { if context.ctrl_c.load(Ordering::SeqCst) {
context.ctrl_c.store(false, Ordering::SeqCst); context.ctrl_c.store(false, Ordering::SeqCst);
@ -273,8 +303,14 @@ pub async fn cli(context: EvaluationContext, options: Options) -> Result<(), Box
let line = match convert_rustyline_result_to_string(readline) { let line = match convert_rustyline_result_to_string(readline) {
LineResult::Success(_) => { LineResult::Success(_) => {
run_options = run_options.span_offset(line_start); process_script(
process_script(&session_text[line_start..], &run_options, &context).await &session_text[line_start..],
&context,
false,
line_start,
true,
)
.await
} }
x => x, x => x,
}; };
@ -465,14 +501,14 @@ fn current_branch() -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nu_engine::{basic_evaluation_context, filesystem::filesystem_shell::FilesystemShellMode}; use nu_engine::basic_evaluation_context;
#[quickcheck] #[quickcheck]
fn quickcheck_parse(data: String) -> bool { fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(&data, 0); let (tokens, err) = nu_parser::lex(&data, 0);
let (lite_block, err2) = nu_parser::parse_block(tokens); let (lite_block, err2) = nu_parser::parse_block(tokens);
if err.is_none() && err2.is_none() { if err.is_none() && err2.is_none() {
let context = basic_evaluation_context(FilesystemShellMode::Cli).unwrap(); let context = basic_evaluation_context().unwrap();
let _ = nu_parser::classify_block(&lite_block, &context.scope); let _ = nu_parser::classify_block(&lite_block, &context.scope);
} }
true true

View file

@ -22,8 +22,8 @@ pub mod types;
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
pub use crate::cli::cli; pub use crate::cli::cli;
pub use crate::cli::Options;
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file}; pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
pub use crate::cli::{NuScript, Options};
pub use nu_command::commands::default_context::create_default_context; pub use nu_command::commands::default_context::create_default_context;
pub use nu_data::config; pub use nu_data::config;

View file

@ -150,7 +150,6 @@ impl rustyline::Helper for Helper {}
mod tests { mod tests {
use super::*; use super::*;
use nu_engine::basic_evaluation_context; use nu_engine::basic_evaluation_context;
use nu_engine::filesystem::filesystem_shell::FilesystemShellMode;
use rustyline::completion::Completer; use rustyline::completion::Completer;
use rustyline::line_buffer::LineBuffer; use rustyline::line_buffer::LineBuffer;
@ -164,10 +163,7 @@ mod tests {
buffer.insert_str(0, text); buffer.insert_str(0, text);
buffer.set_pos(text.len() - 1); buffer.set_pos(text.len() - 1);
let helper = Helper::new( let helper = Helper::new(basic_evaluation_context().unwrap(), None);
basic_evaluation_context(FilesystemShellMode::Cli).unwrap(),
None,
);
helper.update(&mut buffer, "cd ".len(), &replacement); helper.update(&mut buffer, "cd ".len(), &replacement);
@ -187,10 +183,7 @@ mod tests {
buffer.insert_str(0, text); buffer.insert_str(0, text);
buffer.set_pos(text.len() - 30); buffer.set_pos(text.len() - 30);
let helper = Helper::new( let helper = Helper::new(basic_evaluation_context().unwrap(), None);
basic_evaluation_context(FilesystemShellMode::Cli).unwrap(),
None,
);
helper.update(&mut buffer, "cd ".len(), &replacement); helper.update(&mut buffer, "cd ".len(), &replacement);

View file

@ -505,8 +505,6 @@ mod tests {
}; };
#[cfg(feature = "which")] #[cfg(feature = "which")]
use super::{run_external_command, InputStream}; use super::{run_external_command, InputStream};
#[cfg(feature = "which")]
use nu_engine::filesystem::filesystem_shell::FilesystemShellMode;
#[cfg(feature = "which")] #[cfg(feature = "which")]
use futures::executor::block_on; use futures::executor::block_on;
@ -535,8 +533,8 @@ mod tests {
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build(); let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
let input = InputStream::empty(); let input = InputStream::empty();
let mut ctx = basic_evaluation_context(FilesystemShellMode::Cli) let mut ctx =
.expect("There was a problem creating a basic context."); basic_evaluation_context().expect("There was a problem creating a basic context.");
assert!( assert!(
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout) run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)

View file

@ -1,13 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::basic_evaluation_context;
use nu_engine::whole_stream_command; use nu_engine::whole_stream_command;
use nu_engine::{basic_evaluation_context, filesystem::filesystem_shell::FilesystemShellMode};
use std::error::Error; use std::error::Error;
pub fn create_default_context( pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
mode: FilesystemShellMode, let context = basic_evaluation_context()?;
interactive: bool,
) -> Result<EvaluationContext, Box<dyn Error>> {
let context = basic_evaluation_context(mode)?;
{ {
use crate::commands::*; use crate::commands::*;

View file

@ -1,11 +1,9 @@
use std::path::PathBuf;
use crate::prelude::*; use crate::prelude::*;
use nu_engine::{script, WholeStreamCommand}; use nu_engine::{script, WholeStreamCommand};
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_parser::expand_path; use nu_parser::expand_path;
use nu_protocol::{NuScript, RunScriptOptions, Signature, SyntaxShape}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
pub struct Source; pub struct Source;
@ -46,12 +44,27 @@ pub async fn source(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = EvaluationContext::from_args(&args); let ctx = EvaluationContext::from_args(&args);
let (SourceArgs { filename }, _) = args.process().await?; let (SourceArgs { filename }, _) = args.process().await?;
let script = NuScript::File(PathBuf::from(expand_path(&filename.item).to_string())); // Note: this is a special case for setting the context from a command
let options = RunScriptOptions::default() // In this case, if we don't set it now, we'll lose the scope that this
.use_existing_scope(true) // variable should be set into.
.redirect_stdin(true) let contents = std::fs::read_to_string(expand_path(&filename.item).into_owned());
.exit_on_error(false); match contents {
script::run_script(script, &options, &ctx).await; Ok(contents) => {
let result = script::run_script_standalone(contents, true, &ctx, false).await;
Ok(OutputStream::empty()) if let Err(err) = result {
ctx.error(err.into());
}
Ok(OutputStream::empty())
}
Err(_) => {
ctx.error(ShellError::labeled_error(
"Can't load file to source",
"can't load file",
filename.span(),
));
Ok(OutputStream::empty())
}
}
} }

View file

@ -6,7 +6,6 @@ mod stub_generate;
use double_echo::Command as DoubleEcho; use double_echo::Command as DoubleEcho;
use double_ls::Command as DoubleLs; use double_ls::Command as DoubleLs;
use nu_engine::filesystem::filesystem_shell::FilesystemShellMode;
use stub_generate::{mock_path, Command as StubOpen}; use stub_generate::{mock_path, Command as StubOpen};
use nu_engine::basic_evaluation_context; use nu_engine::basic_evaluation_context;
@ -27,7 +26,7 @@ use futures::executor::block_on;
pub fn test_examples(cmd: Command) -> Result<(), ShellError> { pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples(); let examples = cmd.examples();
let base_context = basic_evaluation_context(FilesystemShellMode::Cli)?; let base_context = basic_evaluation_context()?;
base_context.add_commands(vec![ base_context.add_commands(vec![
// Command Doubles // Command Doubles
@ -93,7 +92,7 @@ pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> { pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
let examples = cmd.examples(); let examples = cmd.examples();
let base_context = basic_evaluation_context(FilesystemShellMode::Cli)?; let base_context = basic_evaluation_context()?;
base_context.add_commands(vec![ base_context.add_commands(vec![
whole_stream_command(Echo {}), whole_stream_command(Echo {}),
@ -150,7 +149,7 @@ pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
pub fn test_anchors(cmd: Command) -> Result<(), ShellError> { pub fn test_anchors(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples(); let examples = cmd.examples();
let base_context = basic_evaluation_context(FilesystemShellMode::Cli)?; let base_context = basic_evaluation_context()?;
base_context.add_commands(vec![ base_context.add_commands(vec![
// Minimal restricted commands to aid in testing // Minimal restricted commands to aid in testing

View file

@ -1,16 +1,14 @@
use crate::EvaluationContext;
use crate::Scope; use crate::Scope;
use crate::{basic_shell_manager, config_holder::ConfigHolder}; use crate::{basic_shell_manager, config_holder::ConfigHolder};
use crate::{env::basic_host::BasicHost, Host}; use crate::{env::basic_host::BasicHost, Host};
use crate::{filesystem::filesystem_shell::FilesystemShellMode, EvaluationContext};
use indexmap::IndexMap; use indexmap::IndexMap;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::error::Error; use std::error::Error;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::Arc; use std::sync::Arc;
pub fn basic_evaluation_context( pub fn basic_evaluation_context() -> Result<EvaluationContext, Box<dyn Error>> {
mode: FilesystemShellMode,
) -> Result<EvaluationContext, Box<dyn Error>> {
let scope = Scope::new(); let scope = Scope::new();
let mut host = BasicHost {}; let mut host = BasicHost {};
let env_vars = host.vars().iter().cloned().collect::<IndexMap<_, _>>(); let env_vars = host.vars().iter().cloned().collect::<IndexMap<_, _>>();
@ -22,7 +20,7 @@ pub fn basic_evaluation_context(
current_errors: Arc::new(Mutex::new(vec![])), current_errors: Arc::new(Mutex::new(vec![])),
ctrl_c: Arc::new(AtomicBool::new(false)), ctrl_c: Arc::new(AtomicBool::new(false)),
configs: Arc::new(Mutex::new(ConfigHolder::new())), configs: Arc::new(Mutex::new(ConfigHolder::new())),
shell_manager: basic_shell_manager::basic_shell_manager(mode)?, shell_manager: basic_shell_manager::basic_shell_manager()?,
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())), windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
}) })
} }

View file

@ -6,9 +6,11 @@ use std::error::Error;
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::Arc; use std::sync::Arc;
pub fn basic_shell_manager(mode: FilesystemShellMode) -> Result<ShellManager, Box<dyn Error>> { pub fn basic_shell_manager() -> Result<ShellManager, Box<dyn Error>> {
Ok(ShellManager { Ok(ShellManager {
current_shell: Arc::new(AtomicUsize::new(0)), current_shell: Arc::new(AtomicUsize::new(0)),
shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic(mode)?)])), shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic(
FilesystemShellMode::Cli,
)?)])),
}) })
} }

View file

@ -7,12 +7,12 @@ use crate::{command_args::CommandArgs, script};
use log::trace; use log::trace;
use nu_data::config::{self, Conf, NuConfig}; use nu_data::config::{self, Conf, NuConfig};
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir, ConfigPath, NuScript, RunScriptOptions}; use nu_protocol::{hir, ConfigPath};
use nu_source::{Span, Tag}; use nu_source::{Span, Tag};
use nu_stream::{InputStream, OutputStream}; use nu_stream::{InputStream, OutputStream};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::atomic::AtomicBool;
use std::{path::Path, sync::Arc}; use std::{path::Path, sync::Arc};
use std::{path::PathBuf, sync::atomic::AtomicBool};
#[derive(Clone)] #[derive(Clone)]
pub struct EvaluationContext { pub struct EvaluationContext {
@ -177,9 +177,9 @@ impl EvaluationContext {
} }
} }
let options = exit_entry_script_options(&cfg_path); if !startup_scripts.is_empty() {
for script in startup_scripts { self.run_scripts(startup_scripts, cfg_path.get_path().parent())
script::run_script(NuScript::Content(script), &options, self).await; .await;
} }
Ok(()) Ok(())
@ -270,27 +270,32 @@ impl EvaluationContext {
//Run exitscripts with scope frame and cfg still applied //Run exitscripts with scope frame and cfg still applied
if let Some(scripts) = self.scope.get_exitscripts_of_frame_with_tag(&tag) { if let Some(scripts) = self.scope.get_exitscripts_of_frame_with_tag(&tag) {
let options = exit_entry_script_options(&cfg_path); self.run_scripts(scripts, cfg_path.get_path().parent())
for script in scripts { .await;
script::run_script(NuScript::Content(script), &options, self).await;
}
} }
//Unload config //Unload config
self.configs.lock().remove_cfg(&cfg_path); self.configs.lock().remove_cfg(&cfg_path);
self.scope.exit_scope_with_tag(&tag); self.scope.exit_scope_with_tag(&tag);
} }
}
fn exit_entry_script_options(cfg_path: &ConfigPath) -> RunScriptOptions { /// Runs scripts with cwd of dir. If dir is None, this method does nothing.
let root = PathBuf::from("/"); /// Each error is added to `self.current_errors`
RunScriptOptions::default() pub async fn run_scripts(&self, scripts: Vec<String>, dir: Option<&Path>) {
.with_cwd( if let Some(dir) = dir {
cfg_path for script in scripts {
.get_path() match script::run_script_in_dir(script.clone(), dir, &self).await {
.parent() Ok(_) => {}
.map(Path::to_path_buf) Err(e) => {
.unwrap_or(root), let err = ShellError::untagged_runtime_error(format!(
) "Err while executing exitscript. Err was\n{:?}",
.exit_on_error(false) e
));
let text = script.into();
self.host.lock().print_err(err, &text);
}
}
}
}
}
} }

View file

@ -10,7 +10,6 @@ use futures::stream::BoxStream;
use futures::StreamExt; use futures::StreamExt;
use futures_codec::FramedRead; use futures_codec::FramedRead;
use futures_util::TryStreamExt; use futures_util::TryStreamExt;
use log::trace;
use nu_data::config::LocalConfigDiff; use nu_data::config::LocalConfigDiff;
use nu_protocol::{CommandAction, ConfigPath, TaggedDictBuilder, Value}; use nu_protocol::{CommandAction, ConfigPath, TaggedDictBuilder, Value};
use nu_source::{Span, Tag}; use nu_source::{Span, Tag};
@ -302,11 +301,6 @@ impl Shell for FilesystemShell {
if self.is_cli() { if self.is_cli() {
match dunce::canonicalize(self.path()) { match dunce::canonicalize(self.path()) {
Err(e) => { Err(e) => {
trace!(
"Err canonicalize current path: {:?}, err: {:?}",
self.path(),
e
);
let err = ShellError::untagged_runtime_error(format!( let err = ShellError::untagged_runtime_error(format!(
"Could not get absolute path from current fs shell. The error was: {:?}", "Could not get absolute path from current fs shell. The error was: {:?}",
e e

View file

@ -3,22 +3,19 @@ use crate::{MaybeTextCodec, StringOrBinary};
use futures::StreamExt; use futures::StreamExt;
use futures_codec::FramedRead; use futures_codec::FramedRead;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_parser::ParserScope; use nu_protocol::hir::{
use nu_protocol::{ Call, ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments,
hir::{ SpannedExpression,
Call, ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments,
SpannedExpression,
},
NuScript, ReturnSuccess, RunScriptOptions,
}; };
use nu_protocol::{Primitive, UntaggedValue, Value}; use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
use nu_stream::{InputStream, ToInputStream}; use nu_stream::{InputStream, ToInputStream};
use crate::EvaluationContext; use crate::EvaluationContext;
use log::{debug, trace}; use log::{debug, trace};
use nu_source::{Span, Tag, Text}; use nu_source::{Span, Tag, Text};
use std::iter::Iterator;
use std::path::Path; use std::path::Path;
use std::{iter::Iterator, sync::atomic::Ordering}; use std::{error::Error, sync::atomic::Ordering};
#[derive(Debug)] #[derive(Debug)]
pub enum LineResult { pub enum LineResult {
@ -30,7 +27,6 @@ pub enum LineResult {
ClearHistory, ClearHistory,
} }
//TODO is this still needed
fn chomp_newline(s: &str) -> &str { fn chomp_newline(s: &str) -> &str {
if let Some(s) = s.strip_suffix('\n') { if let Some(s) = s.strip_suffix('\n') {
s s
@ -39,106 +35,36 @@ fn chomp_newline(s: &str) -> &str {
} }
} }
/// Runs script `script` configurable by `options` pub async fn run_script_in_dir(
/// All errors are printed out. script: String,
pub async fn run_script(script: NuScript, options: &RunScriptOptions, ctx: &EvaluationContext) { dir: &Path,
let code = match script.get_code() { ctx: &EvaluationContext,
Ok(code) => code, ) -> Result<(), Box<dyn Error>> {
Err(e) => { //Save path before to switch back to it after executing script
ctx.host.lock().print_err(e, &Text::from("".to_string())); let path_before = ctx.shell_manager.path();
return;
}
};
if let Err(e) = setup_shell(options, ctx) { ctx.shell_manager
ctx.host.lock().print_err(e, &Text::from("".to_string())); .set_path(dir.to_string_lossy().to_string());
return; run_script_standalone(script, false, ctx, false).await?;
} ctx.shell_manager.set_path(path_before);
if !options.use_existing_scope {
ctx.scope.enter_scope()
}
let line_result = process_script(&code, options, ctx).await;
evaluate_line_result(line_result, options, ctx).await;
if !options.use_existing_scope {
ctx.scope.exit_scope()
}
//Leave script shell
ctx.shell_manager.remove_at_current();
}
fn setup_shell(options: &RunScriptOptions, ctx: &EvaluationContext) -> Result<(), ShellError> {
//Switch to correct shell
if options.cli_mode {
ctx.shell_manager.enter_cli_mode()?;
} else {
ctx.shell_manager.enter_script_mode()?;
}
//Switch to cwd if given
if let Some(path) = &options.with_cwd {
ctx.shell_manager
.set_path(path.to_string_lossy().to_string());
}
Ok(()) Ok(())
} }
async fn evaluate_line_result(
line_result: LineResult,
options: &RunScriptOptions,
context: &EvaluationContext,
) {
match line_result {
LineResult::Success(line) => {
let error_code = {
let errors = context.current_errors.clone();
let errors = errors.lock();
if errors.len() > 0 {
1
} else {
0
}
};
maybe_print_errors(&context, Text::from(line));
if error_code != 0 && options.exit_on_error {
std::process::exit(error_code);
}
}
LineResult::Error(line, err) => {
context
.host
.lock()
.print_err(err, &Text::from(line.clone()));
maybe_print_errors(&context, Text::from(line));
if options.exit_on_error {
std::process::exit(1);
}
}
_ => {}
}
}
/// Process the line by parsing the text to turn it into commands, classify those commands so that we understand what is being called in the pipeline, and then run this pipeline /// Process the line by parsing the text to turn it into commands, classify those commands so that we understand what is being called in the pipeline, and then run this pipeline
pub async fn process_script( pub async fn process_script(
script_text: &str, script_text: &str,
options: &RunScriptOptions,
ctx: &EvaluationContext, ctx: &EvaluationContext,
redirect_stdin: bool,
span_offset: usize,
cli_mode: bool,
) -> LineResult { ) -> LineResult {
if script_text.trim() == "" { if script_text.trim() == "" {
LineResult::Success(script_text.to_string()) LineResult::Success(script_text.to_string())
} else { } else {
let line = chomp_newline(script_text); let line = chomp_newline(script_text);
let (block, err) = nu_parser::parse(&line, options.span_offset, &ctx.scope); let (block, err) = nu_parser::parse(&line, span_offset, &ctx.scope);
debug!("{:#?}", block); debug!("{:#?}", block);
//println!("{:#?}", pipeline); //println!("{:#?}", pipeline);
@ -153,7 +79,7 @@ pub async fn process_script(
// ...and it doesn't have any arguments // ...and it doesn't have any arguments
// ...and we're in the CLI // ...and we're in the CLI
// ...then change to this directory // ...then change to this directory
if options.cli_mode if cli_mode
&& block.block.len() == 1 && block.block.len() == 1
&& block.block[0].pipelines.len() == 1 && block.block[0].pipelines.len() == 1
&& block.block[0].pipelines[0].list.len() == 1 && block.block[0].pipelines[0].list.len() == 1
@ -234,7 +160,7 @@ pub async fn process_script(
} }
} }
let input_stream = if options.redirect_stdin { let input_stream = if redirect_stdin {
let file = futures::io::AllowStdIo::new(std::io::stdin()); let file = futures::io::AllowStdIo::new(std::io::stdin());
let stream = FramedRead::new(file, MaybeTextCodec::default()).map(|line| { let stream = FramedRead::new(file, MaybeTextCodec::default()).map(|line| {
if let Ok(line) = line { if let Ok(line) = line {
@ -309,3 +235,55 @@ pub async fn process_script(
} }
} }
} }
pub async fn run_script_standalone(
script_text: String,
redirect_stdin: bool,
context: &EvaluationContext,
exit_on_error: bool,
) -> Result<(), Box<dyn Error>> {
context
.shell_manager
.enter_script_mode()
.map_err(Box::new)?;
let line = process_script(&script_text, context, redirect_stdin, 0, false).await;
match line {
LineResult::Success(line) => {
let error_code = {
let errors = context.current_errors.clone();
let errors = errors.lock();
if errors.len() > 0 {
1
} else {
0
}
};
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()));
maybe_print_errors(&context, Text::from(line));
if exit_on_error {
std::process::exit(1);
}
}
_ => {}
}
//exit script mode shell
context.shell_manager.remove_at_current();
Ok(())
}

View file

@ -27,13 +27,6 @@ impl ShellManager {
Ok(()) Ok(())
} }
pub fn enter_cli_mode(&self) -> Result<(), std::io::Error> {
//New fs_shell starting from current path
let fs_shell = FilesystemShell::with_location(self.path(), FilesystemShellMode::Cli)?;
self.insert_at_current(Box::new(fs_shell));
Ok(())
}
pub fn insert_at_current(&self, shell: Box<dyn Shell + Send>) { pub fn insert_at_current(&self, shell: Box<dyn Shell + Send>) {
self.shells.lock().push(shell); self.shells.lock().push(shell);
self.current_shell self.current_shell

View file

@ -6,7 +6,6 @@ pub mod config_path;
pub mod hir; pub mod hir;
mod maybe_owned; mod maybe_owned;
mod return_value; mod return_value;
mod script;
mod signature; mod signature;
mod syntax_shape; mod syntax_shape;
mod type_name; mod type_name;
@ -17,7 +16,6 @@ pub use crate::call_info::{CallInfo, EvaluatedArgs};
pub use crate::config_path::ConfigPath; pub use crate::config_path::ConfigPath;
pub use crate::maybe_owned::MaybeOwned; pub use crate::maybe_owned::MaybeOwned;
pub use crate::return_value::{CommandAction, ReturnSuccess, ReturnValue}; pub use crate::return_value::{CommandAction, ReturnSuccess, ReturnValue};
pub use crate::script::{NuScript, RunScriptOptions};
pub use crate::signature::{NamedType, PositionalType, Signature}; pub use crate::signature::{NamedType, PositionalType, Signature};
pub use crate::syntax_shape::SyntaxShape; pub use crate::syntax_shape::SyntaxShape;
pub use crate::type_name::{PrettyType, ShellTypeName, SpannedTypeName}; pub use crate::type_name::{PrettyType, ShellTypeName, SpannedTypeName};

View file

@ -1,83 +0,0 @@
use std::path::PathBuf;
use nu_errors::ShellError;
/// NuScript is either directly some nu code or
/// a file path to a nu-script file.
pub enum NuScript {
Content(String),
File(PathBuf),
}
impl NuScript {
pub fn get_code(self) -> Result<String, ShellError> {
match self {
NuScript::Content(code) => Ok(code),
NuScript::File(path) => std::fs::read_to_string(path).map_err(|e| {
ShellError::untagged_runtime_error(format!("Reading of script failed with: {}", e))
}),
}
}
}
#[derive(Debug, Clone)]
pub struct RunScriptOptions {
pub with_cwd: Option<PathBuf>,
pub with_stdin: bool,
pub redirect_stdin: bool,
pub exit_on_error: bool,
pub cli_mode: bool,
pub span_offset: usize,
pub use_existing_scope: bool,
}
impl Default for RunScriptOptions {
fn default() -> Self {
Self {
with_cwd: None,
with_stdin: true,
redirect_stdin: false,
exit_on_error: true,
cli_mode: false,
span_offset: 0,
use_existing_scope: false,
}
}
}
impl RunScriptOptions {
pub fn with_cwd(mut self, path: PathBuf) -> Self {
self.with_cwd = Some(path);
self
}
pub fn with_stdin(mut self, stdin: bool) -> Self {
self.with_stdin = stdin;
self
}
pub fn redirect_stdin(mut self, redirect: bool) -> Self {
self.redirect_stdin = redirect;
self
}
pub fn exit_on_error(mut self, exit_on_error: bool) -> Self {
self.exit_on_error = exit_on_error;
self
}
pub fn cli_mode(mut self, cli_mode: bool) -> Self {
self.cli_mode = cli_mode;
self
}
pub fn span_offset(mut self, span_offset: usize) -> Self {
self.span_offset = span_offset;
self
}
pub fn use_existing_scope(mut self, use_existing_scope: bool) -> Self {
self.use_existing_scope = use_existing_scope;
self
}
}

View file

@ -43,6 +43,10 @@ impl Dirs {
pub fn formats(&self) -> PathBuf { pub fn formats(&self) -> PathBuf {
self.fixtures.join("formats") self.fixtures.join("formats")
} }
pub fn config_fixtures(&self) -> PathBuf {
self.fixtures.join("playground/config")
}
} }
impl<'a> Playground<'a> { impl<'a> Playground<'a> {

View file

@ -1,10 +1,8 @@
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg};
use log::LevelFilter; use log::LevelFilter;
use nu_cli::{create_default_context, Options}; use nu_cli::{create_default_context, NuScript, Options};
use nu_command::utils::test_bins as binaries; use nu_command::utils::test_bins as binaries;
use nu_engine::filesystem::filesystem_shell::FilesystemShellMode; use std::error::Error;
use nu_protocol::{NuScript, RunScriptOptions};
use std::{error::Error, path::PathBuf};
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut options = Options::new(); let mut options = Options::new();
@ -150,29 +148,25 @@ fn main() -> Result<(), Box<dyn Error>> {
match matches.values_of("commands") { match matches.values_of("commands") {
None => {} None => {}
Some(values) => { Some(values) => {
options.scripts = values options.scripts = vec![NuScript::code(values)?];
.map(|cmd| NuScript::Content(cmd.to_string()))
.collect(); futures::executor::block_on(nu_cli::run_script_file(options))?;
let mut run_options = script_options_from_matches(&matches);
// we always exit on err
run_options.exit_on_error = true;
futures::executor::block_on(nu_cli::run_script_file(options, run_options))?;
return Ok(()); return Ok(());
} }
} }
match matches.value_of("script") { match matches.value_of("script") {
Some(filepath) => { Some(filepath) => {
options.scripts = vec![NuScript::File(PathBuf::from(filepath))]; let filepath = std::ffi::OsString::from(filepath);
let mut run_options = script_options_from_matches(&matches);
// we always exit on err options.scripts = vec![NuScript::source_file(filepath.as_os_str())?];
run_options.exit_on_error = true;
futures::executor::block_on(nu_cli::run_script_file(options, run_options))?; futures::executor::block_on(nu_cli::run_script_file(options))?;
return Ok(()); return Ok(());
} }
None => { None => {
let context = create_default_context(FilesystemShellMode::Cli, true)?; let context = create_default_context(true)?;
if !matches.is_present("skip-plugins") { if !matches.is_present("skip-plugins") {
let _ = nu_cli::register_plugins(&context); let _ = nu_cli::register_plugins(&context);
@ -192,7 +186,3 @@ fn main() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
fn script_options_from_matches(matches: &ArgMatches) -> RunScriptOptions {
RunScriptOptions::default().with_stdin(matches.is_present("stdin"))
}

View file

@ -11,11 +11,10 @@ mod environment;
mod pipeline; mod pipeline;
#[should_panic]
#[test] #[test]
fn runs_configuration_startup_commands() { fn runs_configuration_startup_commands() {
Playground::setup("init_config_startup_commands_test", |dirs, nu| { Playground::setup("init_config_startup_commands_test", |dirs, nu| {
let file = AbsolutePath::new(dirs.test().join("startup.toml")); let file = AbsolutePath::new(dirs.config_fixtures().join("startup.toml"));
nu.with_config(&file); nu.with_config(&file);