nu_command/filesystem/
cd.rs1use std::path::PathBuf;
2
3use nu_engine::command_prelude::*;
4use nu_protocol::shell_error::{self, io::IoError};
5use nu_utils::filesystem::{PermissionResult, have_permission};
6
7#[derive(Clone)]
8pub struct Cd;
9
10impl Command for Cd {
11 fn name(&self) -> &str {
12 "cd"
13 }
14
15 fn description(&self) -> &str {
16 "Change the current working directory."
17 }
18
19 fn search_terms(&self) -> Vec<&str> {
20 vec!["change", "directory", "dir", "folder", "switch", "chdir"]
21 }
22
23 fn signature(&self) -> nu_protocol::Signature {
24 Signature::build("cd")
25 .input_output_types(vec![(Type::Nothing, Type::Nothing)])
26 .switch("physical", "Use the physical directory structure; resolve symbolic links before processing instances of ..", Some('P'))
27 .optional("path", SyntaxShape::Directory, "The path to change to.")
28 .allow_variants_without_examples(true)
29 .category(Category::FileSystem)
30 }
31
32 fn run(
33 &self,
34 engine_state: &EngineState,
35 stack: &mut Stack,
36 call: &Call,
37 _input: PipelineData,
38 ) -> Result<PipelineData, ShellError> {
39 let physical = call.has_flag(engine_state, stack, "physical")?;
40 let path_val = call
41 .opt::<Spanned<String>>(engine_state, stack, 0)?
42 .map(|p| p.map(nu_utils::strip_ansi_string_unlikely));
43
44 let cwd = engine_state
47 .cwd(Some(stack))
48 .ok()
49 .or_else(nu_path::home_dir)
50 .map(|path| path.into_std_path_buf())
51 .unwrap_or_default();
52
53 let path = match path_val {
54 Some(v) => {
55 if v.item == "-" {
56 if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") {
57 oldpwd.to_path()?
58 } else {
59 cwd
60 }
61 } else {
62 let path_no_whitespace =
64 &v.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
65
66 if physical {
68 if let Ok(path) = nu_path::canonicalize_with(path_no_whitespace, &cwd) {
69 if !path.is_dir() {
70 return Err(shell_error::io::IoError::new(
71 shell_error::io::ErrorKind::from_std(
72 std::io::ErrorKind::NotADirectory,
73 ),
74 v.span,
75 None,
76 )
77 .into());
78 };
79 path
80 } else {
81 return Err(shell_error::io::IoError::new(
82 ErrorKind::DirectoryNotFound,
83 v.span,
84 PathBuf::from(path_no_whitespace),
85 )
86 .into());
87 }
88 } else {
89 let path = nu_path::expand_path_with(path_no_whitespace, &cwd, true);
90 if !path.exists() {
91 return Err(shell_error::io::IoError::new(
92 ErrorKind::DirectoryNotFound,
93 v.span,
94 PathBuf::from(path_no_whitespace),
95 )
96 .into());
97 };
98 if !path.is_dir() {
99 return Err(shell_error::io::IoError::new(
100 shell_error::io::ErrorKind::from_std(
101 std::io::ErrorKind::NotADirectory,
102 ),
103 v.span,
104 path,
105 )
106 .into());
107 };
108 path
109 }
110 }
111 }
112 None => nu_path::expand_tilde("~"),
113 };
114
115 if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") {
118 stack.add_env_var("OLDPWD".into(), oldpwd.clone())
119 }
120
121 match have_permission(&path) {
122 PermissionResult::PermissionOk => {
125 stack.set_cwd(path)?;
126 Ok(PipelineData::empty())
127 }
128 PermissionResult::PermissionDenied => Err(IoError::new(
129 shell_error::io::ErrorKind::from_std(std::io::ErrorKind::PermissionDenied),
130 call.head,
131 path,
132 )
133 .into()),
134 }
135 }
136
137 fn examples(&self) -> Vec<Example<'_>> {
138 vec![
139 Example {
140 description: "Change to your home directory.",
141 example: "cd ~",
142 result: None,
143 },
144 Example {
145 description: r#"Change to the previous working directory (same as "cd $env.OLDPWD")."#,
146 example: "cd -",
147 result: None,
148 },
149 Example {
150 description: "Changing directory with a custom command requires 'def --env'.",
151 example: "def --env gohome [] { cd ~ }",
152 result: None,
153 },
154 Example {
155 description: "Move two directories up in the tree (the parent directory's parent). Additional dots can be added for additional levels.",
156 example: "cd ...",
157 result: None,
158 },
159 Example {
160 description: "The cd command itself is often optional. Simply entering a path to a directory will cd to it.",
161 example: "/home",
162 result: None,
163 },
164 ]
165 }
166}