forked from extern/nushell
Add and use new Signals
struct (#13314)
# Description This PR introduces a new `Signals` struct to replace our adhoc passing around of `ctrlc: Option<Arc<AtomicBool>>`. Doing so has a few benefits: - We can better enforce when/where resetting or triggering an interrupt is allowed. - Consolidates `nu_utils::ctrl_c::was_pressed` and other ad-hoc re-implementations into a single place: `Signals::check`. - This allows us to add other types of signals later if we want. E.g., exiting or suspension. - Similarly, we can more easily change the underlying implementation if we need to in the future. - Places that used to have a `ctrlc` of `None` now use `Signals::empty()`, so we can double check these usages for correctness in the future.
This commit is contained in:
@@ -3,10 +3,9 @@ use crate::{DirBuilder, DirInfo, FileInfo};
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_glob::Pattern;
|
||||
use nu_protocol::NuGlob;
|
||||
use nu_protocol::{NuGlob, Signals};
|
||||
use serde::Deserialize;
|
||||
use std::path::Path;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Du;
|
||||
@@ -120,8 +119,8 @@ impl Command for Du {
|
||||
min_size,
|
||||
};
|
||||
Ok(
|
||||
du_for_one_pattern(args, ¤t_dir, tag, engine_state.ctrlc.clone())?
|
||||
.into_pipeline_data(tag, engine_state.ctrlc.clone()),
|
||||
du_for_one_pattern(args, ¤t_dir, tag, engine_state.signals())?
|
||||
.into_pipeline_data(tag, engine_state.signals().clone()),
|
||||
)
|
||||
}
|
||||
Some(paths) => {
|
||||
@@ -139,7 +138,7 @@ impl Command for Du {
|
||||
args,
|
||||
¤t_dir,
|
||||
tag,
|
||||
engine_state.ctrlc.clone(),
|
||||
engine_state.signals(),
|
||||
)?)
|
||||
}
|
||||
|
||||
@@ -147,7 +146,7 @@ impl Command for Du {
|
||||
Ok(result_iters
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.into_pipeline_data(tag, engine_state.ctrlc.clone()))
|
||||
.into_pipeline_data(tag, engine_state.signals().clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,8 +163,8 @@ impl Command for Du {
|
||||
fn du_for_one_pattern(
|
||||
args: DuArgs,
|
||||
current_dir: &Path,
|
||||
call_span: Span,
|
||||
ctrl_c: Option<Arc<AtomicBool>>,
|
||||
span: Span,
|
||||
signals: &Signals,
|
||||
) -> Result<impl Iterator<Item = Value> + Send, ShellError> {
|
||||
let exclude = args.exclude.map_or(Ok(None), move |x| {
|
||||
Pattern::new(x.item.as_ref())
|
||||
@@ -178,7 +177,7 @@ fn du_for_one_pattern(
|
||||
|
||||
let include_files = args.all;
|
||||
let mut paths = match args.path {
|
||||
Some(p) => nu_engine::glob_from(&p, current_dir, call_span, None),
|
||||
Some(p) => nu_engine::glob_from(&p, current_dir, span, None),
|
||||
// The * pattern should never fail.
|
||||
None => nu_engine::glob_from(
|
||||
&Spanned {
|
||||
@@ -186,7 +185,7 @@ fn du_for_one_pattern(
|
||||
span: Span::unknown(),
|
||||
},
|
||||
current_dir,
|
||||
call_span,
|
||||
span,
|
||||
None,
|
||||
),
|
||||
}
|
||||
@@ -205,7 +204,7 @@ fn du_for_one_pattern(
|
||||
let min_size = args.min_size.map(|f| f.item as u64);
|
||||
|
||||
let params = DirBuilder {
|
||||
tag: call_span,
|
||||
tag: span,
|
||||
min: min_size,
|
||||
deref,
|
||||
exclude,
|
||||
@@ -217,13 +216,13 @@ fn du_for_one_pattern(
|
||||
match p {
|
||||
Ok(a) => {
|
||||
if a.is_dir() {
|
||||
output.push(DirInfo::new(a, ¶ms, max_depth, ctrl_c.clone()).into());
|
||||
} else if let Ok(v) = FileInfo::new(a, deref, call_span) {
|
||||
output.push(DirInfo::new(a, ¶ms, max_depth, span, signals)?.into());
|
||||
} else if let Ok(v) = FileInfo::new(a, deref, span) {
|
||||
output.push(v.into());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
output.push(Value::error(e, call_span));
|
||||
output.push(Value::error(e, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use nu_protocol::Signals;
|
||||
use wax::{Glob as WaxGlob, WalkBehavior, WalkEntry};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -125,7 +125,6 @@ impl Command for Glob {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let span = call.head;
|
||||
let glob_pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let depth = call.get_flag(engine_state, stack, "depth")?;
|
||||
@@ -216,7 +215,14 @@ impl Command for Glob {
|
||||
inner: vec![],
|
||||
})?
|
||||
.flatten();
|
||||
glob_to_value(ctrlc, glob_results, no_dirs, no_files, no_symlinks, span)
|
||||
glob_to_value(
|
||||
engine_state.signals(),
|
||||
glob_results,
|
||||
no_dirs,
|
||||
no_files,
|
||||
no_symlinks,
|
||||
span,
|
||||
)
|
||||
} else {
|
||||
let glob_results = glob
|
||||
.walk_with_behavior(
|
||||
@@ -227,12 +233,19 @@ impl Command for Glob {
|
||||
},
|
||||
)
|
||||
.flatten();
|
||||
glob_to_value(ctrlc, glob_results, no_dirs, no_files, no_symlinks, span)
|
||||
glob_to_value(
|
||||
engine_state.signals(),
|
||||
glob_results,
|
||||
no_dirs,
|
||||
no_files,
|
||||
no_symlinks,
|
||||
span,
|
||||
)
|
||||
}?;
|
||||
|
||||
Ok(result
|
||||
.into_iter()
|
||||
.into_pipeline_data(span, engine_state.ctrlc.clone()))
|
||||
.into_pipeline_data(span, engine_state.signals().clone()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +265,7 @@ fn convert_patterns(columns: &[Value]) -> Result<Vec<String>, ShellError> {
|
||||
}
|
||||
|
||||
fn glob_to_value<'a>(
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
signals: &Signals,
|
||||
glob_results: impl Iterator<Item = WalkEntry<'a>>,
|
||||
no_dirs: bool,
|
||||
no_files: bool,
|
||||
@@ -261,10 +274,7 @@ fn glob_to_value<'a>(
|
||||
) -> Result<Vec<Value>, ShellError> {
|
||||
let mut result: Vec<Value> = Vec::new();
|
||||
for entry in glob_results {
|
||||
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
||||
result.clear();
|
||||
return Err(ShellError::InterruptedByUser { span: None });
|
||||
}
|
||||
signals.check(span)?;
|
||||
let file_type = entry.file_type();
|
||||
|
||||
if !(no_dirs && file_type.is_dir()
|
||||
|
@@ -6,14 +6,13 @@ use nu_engine::glob_from;
|
||||
use nu_engine::{command_prelude::*, env::current_dir};
|
||||
use nu_glob::MatchOptions;
|
||||
use nu_path::expand_to_real_path;
|
||||
use nu_protocol::{DataSource, NuGlob, PipelineMetadata};
|
||||
use nu_protocol::{DataSource, NuGlob, PipelineMetadata, Signals};
|
||||
use pathdiff::diff_paths;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
@@ -93,7 +92,6 @@ impl Command for Ls {
|
||||
let du = call.has_flag(engine_state, stack, "du")?;
|
||||
let directory = call.has_flag(engine_state, stack, "directory")?;
|
||||
let use_mime_type = call.has_flag(engine_state, stack, "mime-type")?;
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let call_span = call.head;
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
@@ -116,10 +114,10 @@ impl Command for Ls {
|
||||
Some(pattern_arg)
|
||||
};
|
||||
match input_pattern_arg {
|
||||
None => Ok(ls_for_one_pattern(None, args, ctrl_c.clone(), cwd)?
|
||||
None => Ok(ls_for_one_pattern(None, args, engine_state.signals(), cwd)?
|
||||
.into_pipeline_data_with_metadata(
|
||||
call_span,
|
||||
ctrl_c,
|
||||
engine_state.signals().clone(),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Ls,
|
||||
content_type: None,
|
||||
@@ -131,7 +129,7 @@ impl Command for Ls {
|
||||
result_iters.push(ls_for_one_pattern(
|
||||
Some(pat),
|
||||
args,
|
||||
ctrl_c.clone(),
|
||||
engine_state.signals(),
|
||||
cwd.clone(),
|
||||
)?)
|
||||
}
|
||||
@@ -143,7 +141,7 @@ impl Command for Ls {
|
||||
.flatten()
|
||||
.into_pipeline_data_with_metadata(
|
||||
call_span,
|
||||
ctrl_c,
|
||||
engine_state.signals().clone(),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Ls,
|
||||
content_type: None,
|
||||
@@ -215,7 +213,7 @@ impl Command for Ls {
|
||||
fn ls_for_one_pattern(
|
||||
pattern_arg: Option<Spanned<NuGlob>>,
|
||||
args: Args,
|
||||
ctrl_c: Option<Arc<AtomicBool>>,
|
||||
signals: &Signals,
|
||||
cwd: PathBuf,
|
||||
) -> Result<Box<dyn Iterator<Item = Value> + Send>, ShellError> {
|
||||
let Args {
|
||||
@@ -342,7 +340,7 @@ fn ls_for_one_pattern(
|
||||
|
||||
let mut hidden_dirs = vec![];
|
||||
|
||||
let one_ctrl_c = ctrl_c.clone();
|
||||
let signals = signals.clone();
|
||||
Ok(Box::new(paths_peek.filter_map(move |x| match x {
|
||||
Ok(path) => {
|
||||
let metadata = match std::fs::symlink_metadata(&path) {
|
||||
@@ -412,7 +410,7 @@ fn ls_for_one_pattern(
|
||||
call_span,
|
||||
long,
|
||||
du,
|
||||
one_ctrl_c.clone(),
|
||||
&signals,
|
||||
use_mime_type,
|
||||
);
|
||||
match entry {
|
||||
@@ -474,7 +472,6 @@ fn path_contains_hidden_folder(path: &Path, folders: &[PathBuf]) -> bool {
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
pub fn get_file_type(md: &std::fs::Metadata, display_name: &str, use_mime_type: bool) -> String {
|
||||
let ft = md.file_type();
|
||||
@@ -523,7 +520,7 @@ pub(crate) fn dir_entry_dict(
|
||||
span: Span,
|
||||
long: bool,
|
||||
du: bool,
|
||||
ctrl_c: Option<Arc<AtomicBool>>,
|
||||
signals: &Signals,
|
||||
use_mime_type: bool,
|
||||
) -> Result<Value, ShellError> {
|
||||
#[cfg(windows)]
|
||||
@@ -618,7 +615,7 @@ pub(crate) fn dir_entry_dict(
|
||||
if md.is_dir() {
|
||||
if du {
|
||||
let params = DirBuilder::new(Span::new(0, 2), None, false, None, false);
|
||||
let dir_size = DirInfo::new(filename, ¶ms, None, ctrl_c).get_size();
|
||||
let dir_size = DirInfo::new(filename, ¶ms, None, span, signals)?.get_size();
|
||||
|
||||
Value::filesize(dir_size as i64, span)
|
||||
} else {
|
||||
|
@@ -51,7 +51,6 @@ impl Command for Open {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let raw = call.has_flag(engine_state, stack, "raw")?;
|
||||
let call_span = call.head;
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
@@ -122,8 +121,12 @@ impl Command for Open {
|
||||
} else {
|
||||
#[cfg(feature = "sqlite")]
|
||||
if !raw {
|
||||
let res = SQLiteDatabase::try_from_path(path, arg_span, ctrlc.clone())
|
||||
.map(|db| db.into_value(call.head).into_pipeline_data());
|
||||
let res = SQLiteDatabase::try_from_path(
|
||||
path,
|
||||
arg_span,
|
||||
engine_state.signals().clone(),
|
||||
)
|
||||
.map(|db| db.into_value(call.head).into_pipeline_data());
|
||||
|
||||
if res.is_ok() {
|
||||
return res;
|
||||
@@ -144,7 +147,7 @@ impl Command for Open {
|
||||
};
|
||||
|
||||
let stream = PipelineData::ByteStream(
|
||||
ByteStream::file(file, call_span, ctrlc.clone()),
|
||||
ByteStream::file(file, call_span, engine_state.signals().clone()),
|
||||
Some(PipelineMetadata {
|
||||
data_source: DataSource::FilePath(path.to_path_buf()),
|
||||
content_type: None,
|
||||
@@ -203,7 +206,7 @@ impl Command for Open {
|
||||
Ok(output
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.into_pipeline_data(call_span, ctrlc))
|
||||
.into_pipeline_data(call_span, engine_state.signals().clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -451,12 +451,7 @@ fn rm(
|
||||
});
|
||||
|
||||
for result in iter {
|
||||
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
||||
return Err(ShellError::InterruptedByUser {
|
||||
span: Some(call.head),
|
||||
});
|
||||
}
|
||||
|
||||
engine_state.signals().check(call.head)?;
|
||||
match result {
|
||||
Ok(None) => {}
|
||||
Ok(Some(msg)) => eprintln!("{msg}"),
|
||||
|
@@ -5,15 +5,14 @@ use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::{
|
||||
ast::{Expr, Expression},
|
||||
byte_stream::copy_with_interrupt,
|
||||
byte_stream::copy_with_signals,
|
||||
process::ChildPipe,
|
||||
ByteStreamSource, DataSource, OutDest, PipelineMetadata,
|
||||
ByteStreamSource, DataSource, OutDest, PipelineMetadata, Signals,
|
||||
};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufRead, BufReader, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
thread,
|
||||
};
|
||||
|
||||
@@ -120,30 +119,30 @@ impl Command for Save {
|
||||
)?;
|
||||
|
||||
let size = stream.known_size();
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let signals = engine_state.signals();
|
||||
|
||||
match stream.into_source() {
|
||||
ByteStreamSource::Read(read) => {
|
||||
stream_to_file(read, size, ctrlc, file, span, progress)?;
|
||||
stream_to_file(read, size, signals, file, span, progress)?;
|
||||
}
|
||||
ByteStreamSource::File(source) => {
|
||||
stream_to_file(source, size, ctrlc, file, span, progress)?;
|
||||
stream_to_file(source, size, signals, file, span, progress)?;
|
||||
}
|
||||
ByteStreamSource::Child(mut child) => {
|
||||
fn write_or_consume_stderr(
|
||||
stderr: ChildPipe,
|
||||
file: Option<File>,
|
||||
span: Span,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
signals: &Signals,
|
||||
progress: bool,
|
||||
) -> Result<(), ShellError> {
|
||||
if let Some(file) = file {
|
||||
match stderr {
|
||||
ChildPipe::Pipe(pipe) => {
|
||||
stream_to_file(pipe, None, ctrlc, file, span, progress)
|
||||
stream_to_file(pipe, None, signals, file, span, progress)
|
||||
}
|
||||
ChildPipe::Tee(tee) => {
|
||||
stream_to_file(tee, None, ctrlc, file, span, progress)
|
||||
stream_to_file(tee, None, signals, file, span, progress)
|
||||
}
|
||||
}?
|
||||
} else {
|
||||
@@ -163,14 +162,14 @@ impl Command for Save {
|
||||
// delegate a thread to redirect stderr to result.
|
||||
let handler = stderr
|
||||
.map(|stderr| {
|
||||
let ctrlc = ctrlc.clone();
|
||||
let signals = signals.clone();
|
||||
thread::Builder::new().name("stderr saver".into()).spawn(
|
||||
move || {
|
||||
write_or_consume_stderr(
|
||||
stderr,
|
||||
stderr_file,
|
||||
span,
|
||||
ctrlc,
|
||||
&signals,
|
||||
progress,
|
||||
)
|
||||
},
|
||||
@@ -181,10 +180,10 @@ impl Command for Save {
|
||||
|
||||
let res = match stdout {
|
||||
ChildPipe::Pipe(pipe) => {
|
||||
stream_to_file(pipe, None, ctrlc, file, span, progress)
|
||||
stream_to_file(pipe, None, signals, file, span, progress)
|
||||
}
|
||||
ChildPipe::Tee(tee) => {
|
||||
stream_to_file(tee, None, ctrlc, file, span, progress)
|
||||
stream_to_file(tee, None, signals, file, span, progress)
|
||||
}
|
||||
};
|
||||
if let Some(h) = handler {
|
||||
@@ -202,7 +201,7 @@ impl Command for Save {
|
||||
stderr,
|
||||
stderr_file,
|
||||
span,
|
||||
ctrlc,
|
||||
signals,
|
||||
progress,
|
||||
)?;
|
||||
}
|
||||
@@ -510,7 +509,7 @@ fn get_files(
|
||||
fn stream_to_file(
|
||||
source: impl Read,
|
||||
known_size: Option<u64>,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
signals: &Signals,
|
||||
mut file: File,
|
||||
span: Span,
|
||||
progress: bool,
|
||||
@@ -526,9 +525,9 @@ fn stream_to_file(
|
||||
let mut reader = BufReader::new(source);
|
||||
|
||||
let res = loop {
|
||||
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
||||
if let Err(err) = signals.check(span) {
|
||||
bar.abandoned_msg("# Cancelled #".to_owned());
|
||||
return Ok(());
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
match reader.fill_buf() {
|
||||
@@ -555,7 +554,7 @@ fn stream_to_file(
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
copy_with_interrupt(source, file, span, ctrlc.as_deref())?;
|
||||
copy_with_signals(source, file, span, signals)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -143,7 +143,6 @@ impl Command for Watch {
|
||||
None => RecursiveMode::Recursive,
|
||||
};
|
||||
|
||||
let ctrlc_ref = &engine_state.ctrlc.clone();
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let mut debouncer = match new_debouncer(debounce_duration, None, tx) {
|
||||
@@ -256,7 +255,7 @@ impl Command for Watch {
|
||||
}
|
||||
Err(RecvTimeoutError::Timeout) => {}
|
||||
}
|
||||
if nu_utils::ctrl_c::was_pressed(ctrlc_ref) {
|
||||
if engine_state.signals().interrupted() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user