nu-path crate refactor (#3730)

* Resolve rebase artifacts

* Remove leftover dependencies on removed feature

* Remove unnecessary 'pub'

* Start taking notes and fooling around

* Split canonicalize to two versions; Add TODOs

One that takes `relative_to` and one that doesn't.
More TODO notes.

* Merge absolutize to and rename resolve_dots

* Add custom absolutize fn and use it in path expand

* Convert a couple of dunce::canonicalize to ours

* Update nu-path description

* Replace all canonicalize with nu-path version

* Remove leftover dunce dependencies

* Fix broken autocd with trailing slash

Trailing slash is preserved *only* in paths that do not contain "." or
"..". This should be fixed in the future to cover all paths but for now
it at least covers basic cases.

* Use dunce::canonicalize for canonicalizing

* Alow cd recovery from non-existent cwd

* Disable removed canonicalize functionality tests

Remove unused import

* Break down nu-path into separate modules

* Remove unused public imports

* Remove abundant cow mapping

* Fix clippy warning

* Reformulate old canonicalize tests to expand_path

They wouldn't work with the new canonicalize.

* Canonicalize also ~ and ndots; Unify path joining

Also, add doc comments in nu_path::expansions.

* Add comment

* Avoid expanding ndots if path is not valid UTF-8

With this change, no lossy path->string conversion should happen in the
nu-path crate.

* Fmt

* Slight expand_tilde refactor; Add doc comments

* Start nu-path integration tests

* Add tests TODO

* Fix docstring typo

* Fix some doc strings

* Add README for nu-path crate

* Add a couple of canonicalize tests

* Add nu-path integration tests

* Add trim trailing slashes tests

* Update nu-path dependency

* Remove unused import

* Regenerate lockfile
This commit is contained in:
Jakub Žádník
2021-08-28 15:59:09 +03:00
committed by GitHub
parent 1c1c58e802
commit d95375d494
38 changed files with 1320 additions and 653 deletions

View File

@ -9,7 +9,7 @@ use crate::{
};
use encoding_rs::Encoding;
use nu_data::config::LocalConfigDiff;
use nu_path::canonicalize;
use nu_path::{canonicalize, canonicalize_with, expand_path_with};
use nu_protocol::{CommandAction, ConfigPath, TaggedDictBuilder, Value};
use nu_source::{Span, Tag};
use nu_stream::{ActionStream, Interruptible, IntoActionStream, OutputStream};
@ -77,7 +77,7 @@ impl FilesystemShell {
path: String,
mode: FilesystemShellMode,
) -> Result<FilesystemShell, std::io::Error> {
let path = canonicalize(std::env::current_dir()?, &path)?;
let path = canonicalize_with(&path, std::env::current_dir()?)?;
let path = path.display().to_string();
let last_path = path.clone();
@ -237,13 +237,20 @@ impl Shell for FilesystemShell {
if target == Path::new("-") {
PathBuf::from(&self.last_path)
} else {
let path = canonicalize(self.path(), target).map_err(|_| {
ShellError::labeled_error(
// Extra expand attempt allows cd from /home/user/non-existent-dir/..
// to /home/user
let path = match canonicalize_with(&target, self.path()) {
Ok(p) => p,
_ => expand_path_with(&target, self.path()),
};
if !path.exists() {
return Err(ShellError::labeled_error(
"Cannot change to directory",
"directory not found",
&tag,
)
})?;
));
}
if !path.is_dir() {
return Err(ShellError::labeled_error(
@ -291,7 +298,7 @@ impl Shell for FilesystemShell {
//Loading local configs in script mode, makes scripts behave different on different
//filesystems and might therefore surprise users. That's why we only load them in cli mode.
if self.is_cli() {
match dunce::canonicalize(self.path()) {
match canonicalize(self.path()) {
Err(e) => {
let err = ShellError::untagged_runtime_error(format!(
"Could not get absolute path from current fs shell. The error was: {:?}",
@ -388,7 +395,7 @@ impl Shell for FilesystemShell {
if entry.is_file() {
let sources = sources.paths_applying_with(|(source_file, _depth_level)| {
if destination.is_dir() {
let mut dest = canonicalize(&path, &dst.item)?;
let mut dest = canonicalize_with(&dst.item, &path)?;
if let Some(name) = entry.file_name() {
dest.push(name);
}
@ -427,7 +434,7 @@ impl Shell for FilesystemShell {
let sources = sources.paths_applying_with(|(source_file, depth_level)| {
let mut dest = destination.clone();
let path = canonicalize(&path, &source_file)?;
let path = canonicalize_with(&source_file, &path)?;
let comps: Vec<_> = path
.components()
@ -773,7 +780,7 @@ impl Shell for FilesystemShell {
fn pwd(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let path = PathBuf::from(self.path());
let p = match dunce::canonicalize(path.as_path()) {
let p = match canonicalize(path.as_path()) {
Ok(p) => p,
Err(_) => {
return Err(ShellError::labeled_error(
@ -792,7 +799,7 @@ impl Shell for FilesystemShell {
fn set_path(&mut self, path: String) {
let pathbuf = PathBuf::from(&path);
let path = match canonicalize(self.path(), pathbuf.as_path()) {
let path = match canonicalize_with(pathbuf.as_path(), self.path()) {
Ok(path) => {
let _ = std::env::set_current_dir(&path);
std::env::set_var("PWD", &path);

View File

@ -1,5 +1,5 @@
use nu_errors::ShellError;
use nu_path::canonicalize;
use nu_path::canonicalize_with;
use std::path::{Path, PathBuf};
#[derive(Default)]
@ -47,7 +47,7 @@ impl FileStructure {
}
fn build(&mut self, src: &Path, lvl: usize) -> Result<(), ShellError> {
let source = canonicalize(std::env::current_dir()?, src)?;
let source = canonicalize_with(src, std::env::current_dir()?)?;
if source.is_dir() {
for entry in std::fs::read_dir(src)? {

View File

@ -1,6 +1,7 @@
use crate::plugin::run_plugin::PluginCommandBuilder;
use log::trace;
use nu_errors::ShellError;
use nu_path::canonicalize;
use nu_plugin::jsonrpc::JsonRpc;
use nu_protocol::{Signature, Value};
use std::io::{BufRead, BufReader, Write};
@ -48,7 +49,7 @@ pub fn build_plugin_command(
let request_raw = serde_json::to_string(&request)?;
trace!(target: "nu::load", "plugin infrastructure config -> path {:#?}, request {:?}", &path, &request_raw);
stdin.write_all(format!("{}\n", request_raw).as_bytes())?;
let path = dunce::canonicalize(path)?;
let path = canonicalize(path)?;
let mut input = String::new();
let result = match reader.read_line(&mut input) {
@ -134,7 +135,7 @@ pub fn scan(
let is_executable = {
#[cfg(windows)]
{
bin_name.ends_with(".exe")
bin_name.ends_with(".exe")
|| bin_name.ends_with(".bat")
|| bin_name.ends_with(".cmd")
|| bin_name.ends_with(".py")

View File

@ -1,7 +1,7 @@
use crate::{evaluate::internal::InternalIterator, maybe_print_errors, run_block, shell::CdArgs};
use crate::{BufCodecReader, MaybeTextCodec, StringOrBinary};
use nu_errors::ShellError;
use nu_path::canonicalize;
use nu_path::{canonicalize_with, trim_trailing_slash};
use nu_protocol::hir::{
Call, ClassifiedCommand, Expression, ExternalRedirection, InternalCommand, Literal,
NamedArguments, SpannedExpression,
@ -66,7 +66,6 @@ pub fn process_script(
let (block, err) = nu_parser::parse(line, span_offset, &ctx.scope);
debug!("{:#?}", block);
//println!("{:#?}", pipeline);
if let Some(failure) = err {
return LineResult::Error(line.to_string(), failure.into());
@ -116,7 +115,7 @@ pub fn process_script(
.as_ref()
.map(NamedArguments::is_empty)
.unwrap_or(true)
&& canonicalize(ctx.shell_manager().path(), name).is_ok()
&& canonicalize_with(name, ctx.shell_manager().path()).is_ok()
&& Path::new(&name).is_dir()
&& !ctx.host().lock().is_external_cmd(name)
{
@ -160,6 +159,8 @@ pub fn process_script(
}
};
let path = trim_trailing_slash(&path);
let cd_args = CdArgs {
path: Some(Tagged {
item: PathBuf::from(path),