nu_command/path/
exists.rs1use super::PathSubcommandArguments;
2#[allow(deprecated)]
3use nu_engine::{command_prelude::*, current_dir, current_dir_const};
4use nu_path::expand_path_with;
5use nu_protocol::{engine::StateWorkingSet, shell_error::io::IoError};
6use std::path::{Path, PathBuf};
7
8struct Arguments {
9 pwd: PathBuf,
10 not_follow_symlink: bool,
11}
12
13impl PathSubcommandArguments for Arguments {}
14
15#[derive(Clone)]
16pub struct PathExists;
17
18impl Command for PathExists {
19 fn name(&self) -> &str {
20 "path exists"
21 }
22
23 fn signature(&self) -> Signature {
24 Signature::build("path exists")
25 .input_output_types(vec![
26 (Type::String, Type::Bool),
27 (
28 Type::List(Box::new(Type::String)),
29 Type::List(Box::new(Type::Bool)),
30 ),
31 ])
32 .switch("no-symlink", "Do not resolve symbolic links", Some('n'))
33 .category(Category::Path)
34 }
35
36 fn description(&self) -> &str {
37 "Check whether a path exists."
38 }
39
40 fn extra_description(&self) -> &str {
41 r#"This only checks if it is possible to either `open` or `cd` to the given path.
42If you need to distinguish dirs and files, please use `path type`.
43Also note that if you don't have a permission to a directory of a path, false will be returned"#
44 }
45
46 fn is_const(&self) -> bool {
47 true
48 }
49
50 fn run(
51 &self,
52 engine_state: &EngineState,
53 stack: &mut Stack,
54 call: &Call,
55 input: PipelineData,
56 ) -> Result<PipelineData, ShellError> {
57 let head = call.head;
58 #[allow(deprecated)]
59 let args = Arguments {
60 pwd: current_dir(engine_state, stack)?,
61 not_follow_symlink: call.has_flag(engine_state, stack, "no-symlink")?,
62 };
63 if matches!(input, PipelineData::Empty) {
65 return Err(ShellError::PipelineEmpty { dst_span: head });
66 }
67 input.map(
68 move |value| super::operate(&exists, &args, value, head),
69 engine_state.signals(),
70 )
71 }
72
73 fn run_const(
74 &self,
75 working_set: &StateWorkingSet,
76 call: &Call,
77 input: PipelineData,
78 ) -> Result<PipelineData, ShellError> {
79 let head = call.head;
80 #[allow(deprecated)]
81 let args = Arguments {
82 pwd: current_dir_const(working_set)?,
83 not_follow_symlink: call.has_flag_const(working_set, "no-symlink")?,
84 };
85 if matches!(input, PipelineData::Empty) {
87 return Err(ShellError::PipelineEmpty { dst_span: head });
88 }
89 input.map(
90 move |value| super::operate(&exists, &args, value, head),
91 working_set.permanent().signals(),
92 )
93 }
94
95 #[cfg(windows)]
96 fn examples(&self) -> Vec<Example> {
97 vec![
98 Example {
99 description: "Check if a file exists",
100 example: "'C:\\Users\\joe\\todo.txt' | path exists",
101 result: Some(Value::test_bool(false)),
102 },
103 Example {
104 description: "Check if files in list exist",
105 example: r"[ C:\joe\todo.txt, C:\Users\doe\todo.txt ] | path exists",
106 result: Some(Value::test_list(vec![
107 Value::test_bool(false),
108 Value::test_bool(false),
109 ])),
110 },
111 ]
112 }
113
114 #[cfg(not(windows))]
115 fn examples(&self) -> Vec<Example> {
116 vec![
117 Example {
118 description: "Check if a file exists",
119 example: "'/home/joe/todo.txt' | path exists",
120 result: Some(Value::test_bool(false)),
121 },
122 Example {
123 description: "Check if files in list exist",
124 example: "[ /home/joe/todo.txt, /home/doe/todo.txt ] | path exists",
125 result: Some(Value::test_list(vec![
126 Value::test_bool(false),
127 Value::test_bool(false),
128 ])),
129 },
130 ]
131 }
132}
133
134fn exists(path: &Path, span: Span, args: &Arguments) -> Value {
135 if path.as_os_str().is_empty() {
136 return Value::bool(false, span);
137 }
138 let path = expand_path_with(path, &args.pwd, true);
139 let exists = if args.not_follow_symlink {
140 std::fs::symlink_metadata(&path).map_or_else(
144 |e| match e.kind() {
145 std::io::ErrorKind::NotFound => Ok(false),
146 _ => Err(e),
147 },
148 |_| Ok(true),
149 )
150 } else {
151 Ok(path.exists())
152 };
153 Value::bool(
154 match exists {
155 Ok(exists) => exists,
156 Err(err) => return Value::error(IoError::new(err, span, path).into(), span),
157 },
158 span,
159 )
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_examples() {
168 use crate::test_examples;
169
170 test_examples(PathExists {})
171 }
172}