mirror of
https://github.com/nushell/nushell.git
synced 2024-11-26 10:23:52 +01:00
Path expand fixes (#3505)
* Throw an error if path failed to expand Previously, it just repeated the non-expanded path. * Allow expanding non-existent paths This commit has a strange error in examples. * Specify span manually in examples; Add an example * Expand relative path without requiring cwd * Remove redundant tilde expansion This makes the tilde expansion in relative paths dependant on "dirs" feature. * Add missing example result * Adjust path expand description * Fix import error with missing feature
This commit is contained in:
parent
57a009b8e6
commit
82d69305b6
@ -1,13 +1,18 @@
|
||||
use super::{operate, PathSubcommandArguments};
|
||||
use crate::prelude::*;
|
||||
#[cfg(feature = "dirs")]
|
||||
use nu_engine::filesystem::path::expand_tilde;
|
||||
use nu_engine::filesystem::path::resolve_dots;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use std::path::{Path, PathBuf};
|
||||
use nu_source::Span;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct PathExpand;
|
||||
|
||||
struct PathExpandArguments {
|
||||
strict: bool,
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
@ -24,17 +29,23 @@ impl WholeStreamCommand for PathExpand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path expand")
|
||||
.switch(
|
||||
"strict",
|
||||
"Throw an error if the path could not be expanded",
|
||||
Some('s'),
|
||||
)
|
||||
.rest(SyntaxShape::ColumnPath, "Optionally operate by column path")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Expand a path to its absolute form"
|
||||
"Try to expand a path to its absolute form"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
let cmd_args = Arc::new(PathExpandArguments {
|
||||
strict: args.has_flag("strict"),
|
||||
rest: args.rest(0)?,
|
||||
});
|
||||
|
||||
@ -43,32 +54,65 @@ impl WholeStreamCommand for PathExpand {
|
||||
|
||||
#[cfg(windows)]
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Expand relative directories",
|
||||
example: "echo 'C:\\Users\\joe\\foo\\..\\bar' | path expand",
|
||||
result: None,
|
||||
// fails to canonicalize into Some(vec![Value::from("C:\\Users\\joe\\bar")]) due to non-existing path
|
||||
}]
|
||||
vec![
|
||||
Example {
|
||||
description: "Expand an absolute path",
|
||||
example: r"'C:\Users\joe\foo\..\bar' | path expand",
|
||||
result: Some(vec![
|
||||
UntaggedValue::filepath(r"C:\Users\joe\bar").into_value(Span::new(0, 25))
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Expand a relative path",
|
||||
example: r"'foo\..\bar' | path expand",
|
||||
result: Some(vec![
|
||||
UntaggedValue::filepath("bar").into_value(Span::new(0, 12))
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Expand relative directories",
|
||||
example: "echo '/home/joe/foo/../bar' | path expand",
|
||||
result: None,
|
||||
// fails to canonicalize into Some(vec![Value::from("/home/joe/bar")]) due to non-existing path
|
||||
}]
|
||||
vec![
|
||||
Example {
|
||||
description: "Expand an absolute path",
|
||||
example: "'/home/joe/foo/../bar' | path expand",
|
||||
result: Some(vec![
|
||||
UntaggedValue::filepath("/home/joe/bar").into_value(Span::new(0, 22))
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Expand a relative path",
|
||||
example: "'foo/../bar' | path expand",
|
||||
result: Some(vec![
|
||||
UntaggedValue::filepath("bar").into_value(Span::new(0, 12))
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn action(path: &Path, tag: Tag, _args: &PathExpandArguments) -> Value {
|
||||
let ps = path.to_string_lossy();
|
||||
let expanded = shellexpand::tilde(&ps);
|
||||
let path: &Path = expanded.as_ref().as_ref();
|
||||
fn action(path: &Path, tag: Tag, args: &PathExpandArguments) -> Value {
|
||||
if let Ok(p) = dunce::canonicalize(path) {
|
||||
UntaggedValue::filepath(p).into_value(tag)
|
||||
} else if args.strict {
|
||||
Value::error(ShellError::labeled_error(
|
||||
"Could not expand path",
|
||||
"could not be expanded (path might not exist, non-final \
|
||||
component is not a directory, or other cause)",
|
||||
tag.span,
|
||||
))
|
||||
} else {
|
||||
// "best effort" mode, just expand tilde and resolve single/double dots
|
||||
#[cfg(feature = "dirs")]
|
||||
let path = match expand_tilde(path) {
|
||||
Some(expanded) => expanded,
|
||||
None => path.into(),
|
||||
};
|
||||
|
||||
UntaggedValue::filepath(dunce::canonicalize(path).unwrap_or_else(|_| PathBuf::from(path)))
|
||||
.into_value(tag)
|
||||
UntaggedValue::filepath(resolve_dots(&path)).into_value(tag)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,6 +1,25 @@
|
||||
use std::io;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
pub fn resolve_dots<P>(path: P) -> PathBuf
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut result = PathBuf::new();
|
||||
|
||||
path.as_ref()
|
||||
.components()
|
||||
.for_each(|component| match component {
|
||||
Component::ParentDir => {
|
||||
result.pop();
|
||||
}
|
||||
Component::CurDir => {}
|
||||
_ => result.push(component),
|
||||
});
|
||||
|
||||
dunce::simplified(&result).to_path_buf()
|
||||
}
|
||||
|
||||
pub fn absolutize<P, Q>(relative_to: P, path: Q) -> PathBuf
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
@ -67,7 +86,7 @@ where
|
||||
|
||||
// borrowed from here https://stackoverflow.com/questions/54267608/expand-tilde-in-rust-path-idiomatically
|
||||
#[cfg(feature = "dirs")]
|
||||
fn expand_tilde<P: AsRef<Path>>(path_user_input: P) -> Option<PathBuf> {
|
||||
pub fn expand_tilde<P: AsRef<Path>>(path_user_input: P) -> Option<PathBuf> {
|
||||
let p = path_user_input.as_ref();
|
||||
if !p.starts_with("~") {
|
||||
return Some(p.to_path_buf());
|
||||
|
Loading…
Reference in New Issue
Block a user