run_external.rs: use pathdiff::diff_path to handle relative path (#13056)

# Description
This pr is going to use `pathdiff::diff_path`, so we don't need to
handle for relative path by ourselves.

This is also the behavior before the rewritten of run_external.rs

It's a follow up to https://github.com/nushell/nushell/pull/13028

# User-Facing Changes
NaN

# Tests + Formatting
No need to add tests
This commit is contained in:
Wind 2024-06-07 10:14:42 +08:00 committed by GitHub
parent e3a20e90b0
commit 83cf212e20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -8,6 +8,7 @@ use nu_protocol::{
}; };
use nu_system::ForegroundChild; use nu_system::ForegroundChild;
use nu_utils::IgnoreCaseExt; use nu_utils::IgnoreCaseExt;
use pathdiff::diff_paths;
use std::{ use std::{
borrow::Cow, borrow::Cow,
io::Write, io::Write,
@ -343,61 +344,54 @@ fn expand_glob(
// We must use `nu_engine::glob_from` here, in order to ensure we get paths from the correct // We must use `nu_engine::glob_from` here, in order to ensure we get paths from the correct
// dir // dir
let glob = NuGlob::Expand(arg.to_owned()).into_spanned(span); let glob = NuGlob::Expand(arg.to_owned()).into_spanned(span);
let Ok((_prefix, paths)) = nu_engine::glob_from(&glob, cwd, span, None) else { if let Ok((prefix, matches)) = nu_engine::glob_from(&glob, cwd, span, None) {
// If an error occurred, return the original input let mut result = vec![];
return Ok(vec![arg.into()]);
};
// If the first component of the original `arg` string path was '.', that should be preserved for m in matches {
let relative_to_dot = Path::new(arg).starts_with("."); if nu_utils::ctrl_c::was_pressed(interrupt) {
return Err(ShellError::InterruptedByUser { span: Some(span) });
let paths = paths
// Skip over glob failures. These are usually just inaccessible paths.
.flat_map(|path_result| match path_result {
Ok(path) => Some(path),
Err(err) => {
// But internally log them just in case we need to debug this.
log::warn!("Error in run_external::expand_glob(): {}", err);
None
} }
}) if let Ok(arg) = m {
// Make the paths relative to the cwd let arg = resolve_globbed_path_to_cwd_relative(arg, prefix.as_ref(), cwd);
.map(|path| { result.push(arg.to_string_lossy().to_string());
path.strip_prefix(cwd)
.map(|path| path.to_owned())
.unwrap_or(path)
})
// Add './' to relative paths if the original pattern had it
.map(|path| {
if relative_to_dot && path.is_relative() {
Path::new(".").join(path)
} else { } else {
path result.push(arg.into());
} }
}) }
// Convert the paths returned to UTF-8 strings.
//
// FIXME: this fails to return the correct results for non-UTF-8 paths, but we don't support
// those in Nushell yet.
.map(|path| path.to_string_lossy().into_owned())
// Abandon if ctrl-c is pressed
.map(|path| {
if !nu_utils::ctrl_c::was_pressed(interrupt) {
Ok(path)
} else {
Err(ShellError::InterruptedByUser { span: Some(span) })
}
})
.collect::<Result<Vec<String>, ShellError>>()?;
if !paths.is_empty() { // FIXME: do we want to special-case this further? We might accidentally expand when they don't
Ok(paths) // intend to
if result.is_empty() {
result.push(arg.into());
}
Ok(result)
} else { } else {
// If we failed to match, return the original input
Ok(vec![arg.into()]) Ok(vec![arg.into()])
} }
} }
fn resolve_globbed_path_to_cwd_relative(
path: PathBuf,
prefix: Option<&PathBuf>,
cwd: &Path,
) -> PathBuf {
if let Some(prefix) = prefix {
if let Ok(remainder) = path.strip_prefix(prefix) {
let new_prefix = if let Some(pfx) = diff_paths(prefix, cwd) {
pfx
} else {
prefix.to_path_buf()
};
new_prefix.join(remainder)
} else {
path
}
} else {
path
}
}
/// Transforms `--option="value"` into `--option=value`. `value` can be quoted /// Transforms `--option="value"` into `--option=value`. `value` can be quoted
/// with double quotes, single quotes, or backticks. Only removes the outermost /// with double quotes, single quotes, or backticks. Only removes the outermost
/// pair of quotes after the equal sign. /// pair of quotes after the equal sign.
@ -716,10 +710,6 @@ mod test {
assert_eq!(actual, expected); assert_eq!(actual, expected);
let actual = expand_glob("./*.txt", cwd, Span::unknown(), &None).unwrap(); let actual = expand_glob("./*.txt", cwd, Span::unknown(), &None).unwrap();
let expected = vec![
Path::new(".").join("a.txt").to_string_lossy().into_owned(),
Path::new(".").join("b.txt").to_string_lossy().into_owned(),
];
assert_eq!(actual, expected); assert_eq!(actual, expected);
let actual = expand_glob("'*.txt'", cwd, Span::unknown(), &None).unwrap(); let actual = expand_glob("'*.txt'", cwd, Span::unknown(), &None).unwrap();