nushell/crates/nu-path/src/expansions.rs
Wind 87c5f6e455
ls, rm, cp, open, touch, mkdir: Don't expand tilde if input path is quoted string or a variable. (#12232)
# Description
Fixes:  #11887
Fixes: #11626

This pr unify the tilde expand behavior over several filesystem relative
commands. It follows the same rule with glob expansion:
|  command  |  result |
| ----------- |  ------ |
| ls ~/aaa  | expand tilde
| ls "~/aaa"  | don't expand tilde
| let f = "~/aaa"; ls $f | don't expand tilde, if you want to: use `ls
($f \| path expand)`
| let f: glob = "~/aaa"; ls $f | expand tilde, they don't expand on
`mkdir`, `touch` comamnd.

Actually I'm not sure for 4th item, currently it's expanding is just
because it followes the same rule with glob expansion.

### About the change
It changes `expand_path_with` to accept a new argument called
`expand_tilde`, if it's true, expand it, if not, just keep it as `~`
itself.

# User-Facing Changes
After this change, `ls "~/aaa"` won't expand tilde.

# Tests + Formatting
Done
2024-03-25 10:08:38 +08:00

95 lines
3.2 KiB
Rust

use std::io;
use std::path::{Path, PathBuf};
use super::dots::{expand_dots, expand_ndots};
use super::helpers;
use super::tilde::expand_tilde;
// Join a path relative to another path. Paths starting with tilde are considered as absolute.
fn join_path_relative<P, Q>(path: P, relative_to: Q, expand_tilde: bool) -> PathBuf
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = path.as_ref();
let relative_to = relative_to.as_ref();
if path == Path::new(".") {
// Joining a Path with '.' appends a '.' at the end, making the prompt
// more ugly - so we don't do anything, which should result in an equal
// path on all supported systems.
relative_to.into()
} else if path.to_string_lossy().as_ref().starts_with('~') && expand_tilde {
// do not end up with "/some/path/~" or "/some/path/~user"
path.into()
} else {
relative_to.join(path)
}
}
fn canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> {
let path = expand_tilde(path);
let path = expand_ndots(path);
helpers::canonicalize(&path)
}
/// Resolve all symbolic links and all components (tilde, ., .., ...+) and return the path in its
/// absolute form.
///
/// Fails under the same conditions as
/// [`std::fs::canonicalize`](https://doc.rust-lang.org/std/fs/fn.canonicalize.html).
/// The input path is specified relative to another path
pub fn canonicalize_with<P, Q>(path: P, relative_to: Q) -> io::Result<PathBuf>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = join_path_relative(path, relative_to, true);
canonicalize(path)
}
fn expand_path(path: impl AsRef<Path>, need_expand_tilde: bool) -> PathBuf {
let path = if need_expand_tilde {
expand_tilde(path)
} else {
PathBuf::from(path.as_ref())
};
let path = expand_ndots(path);
expand_dots(path)
}
/// Resolve only path components (tilde, ., .., ...+), if possible.
///
/// The function works in a "best effort" mode: It does not fail but rather returns the unexpanded
/// version if the expansion is not possible.
///
/// Furthermore, unlike canonicalize(), it does not use sys calls (such as readlink).
///
/// Does not convert to absolute form nor does it resolve symlinks.
/// The input path is specified relative to another path
pub fn expand_path_with<P, Q>(path: P, relative_to: Q, expand_tilde: bool) -> PathBuf
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = join_path_relative(path, relative_to, expand_tilde);
expand_path(path, expand_tilde)
}
/// Resolve to a path that is accepted by the system and no further - tilde is expanded, and ndot path components are expanded.
///
/// This function will take a leading tilde path component, and expand it to the user's home directory;
/// it will also expand any path elements consisting of only dots into the correct number of `..` path elements.
/// It does not do any normalization except to what will be accepted by Path::open,
/// and it does not touch the system at all, except for getting the home directory of the current user.
pub fn expand_to_real_path<P>(path: P) -> PathBuf
where
P: AsRef<Path>,
{
let path = expand_tilde(path);
expand_ndots(path)
}