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