1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
use super::{operate, PathSubcommandArguments}; use crate::prelude::*; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; use nu_path::expand_path; use nu_protocol::{ColumnPath, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Span; use std::{borrow::Cow, path::Path}; pub struct PathExpand; struct PathExpandArguments { strict: bool, rest: Vec<ColumnPath>, } impl PathSubcommandArguments for PathExpandArguments { fn get_column_paths(&self) -> &Vec<ColumnPath> { &self.rest } } impl WholeStreamCommand for PathExpand { fn name(&self) -> &str { "path expand" } 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 { "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 cmd_args = Arc::new(PathExpandArguments { strict: args.has_flag("strict"), rest: args.rest(0)?, }); Ok(operate(args.input, &action, tag.span, cmd_args)) } #[cfg(windows)] fn examples(&self) -> Vec<Example> { 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 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 { 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 { UntaggedValue::filepath(expand_path(Cow::Borrowed(path))).into_value(tag) } } #[cfg(test)] mod tests { use super::PathExpand; use super::ShellError; #[test] fn examples_work_as_expected() -> Result<(), ShellError> { use crate::examples::test as test_examples; test_examples(PathExpand {}) } }