mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 16:05:01 +02:00
Implement PWD recovery (#12779)
This PR has two parts. The first part is the addition of the `Stack::set_pwd()` API. It strips trailing slashes from paths for convenience, but will reject otherwise bad paths, leaving PWD in a good state. This should reduce the impact of faulty code incorrectly trying to set PWD. (https://github.com/nushell/nushell/pull/12760#issuecomment-2095393012) The second part is implementing a PWD recovery mechanism. PWD can become bad even when we did nothing wrong. For example, Unix allows you to remove any directory when another process might still be using it, which means PWD can just "disappear" under our nose. This PR makes it possible to use `cd` to reset PWD into a good state. Here's a demonstration: ```sh mkdir /tmp/foo cd /tmp/foo # delete "/tmp/foo" in a subshell, because Nushell is smart and refuse to delete PWD nu -c 'cd /; rm -r /tmp/foo' ls # Error: × $env.PWD points to a non-existent directory # help: Use `cd` to reset $env.PWD into a good state cd / pwd # prints / ``` Also, auto-cd should be working again.
This commit is contained in:
@ -931,13 +931,12 @@ impl EngineState {
|
||||
/// directory on the stack that have yet to be merged into the engine state.
|
||||
pub fn cwd(&self, stack: Option<&Stack>) -> Result<PathBuf, ShellError> {
|
||||
// Helper function to create a simple generic error.
|
||||
// Its messages are not especially helpful, but these errors don't occur often, so it's probably fine.
|
||||
fn error(msg: &str) -> Result<PathBuf, ShellError> {
|
||||
fn error(msg: &str, cwd: impl AsRef<Path>) -> Result<PathBuf, ShellError> {
|
||||
Err(ShellError::GenericError {
|
||||
error: msg.into(),
|
||||
msg: "".into(),
|
||||
msg: format!("$env.PWD = {}", cwd.as_ref().display()),
|
||||
span: None,
|
||||
help: None,
|
||||
help: Some("Use `cd` to reset $env.PWD into a good state".into()),
|
||||
inner: vec![],
|
||||
})
|
||||
}
|
||||
@ -967,21 +966,21 @@ impl EngineState {
|
||||
// Technically, a root path counts as "having trailing slashes", but
|
||||
// for the purpose of PWD, a root path is acceptable.
|
||||
if !is_root(&path) && has_trailing_slash(&path) {
|
||||
error("$env.PWD contains trailing slashes")
|
||||
error("$env.PWD contains trailing slashes", path)
|
||||
} else if !path.is_absolute() {
|
||||
error("$env.PWD is not an absolute path")
|
||||
error("$env.PWD is not an absolute path", path)
|
||||
} else if !path.exists() {
|
||||
error("$env.PWD points to a non-existent directory")
|
||||
error("$env.PWD points to a non-existent directory", path)
|
||||
} else if !path.is_dir() {
|
||||
error("$env.PWD points to a non-directory")
|
||||
error("$env.PWD points to a non-directory", path)
|
||||
} else {
|
||||
Ok(path)
|
||||
}
|
||||
} else {
|
||||
error("$env.PWD is not a string")
|
||||
error("$env.PWD is not a string", format!("{pwd:?}"))
|
||||
}
|
||||
} else {
|
||||
error("$env.PWD not found")
|
||||
error("$env.PWD not found", "")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -592,6 +592,40 @@ impl Stack {
|
||||
self.out_dest.pipe_stderr = None;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the PWD environment variable to `path`.
|
||||
///
|
||||
/// This method accepts `path` with trailing slashes, but they're removed
|
||||
/// before writing the value into PWD.
|
||||
pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
|
||||
// Helper function to create a simple generic error.
|
||||
// Its messages are not especially helpful, but these errors don't occur often, so it's probably fine.
|
||||
fn error(msg: &str) -> Result<(), ShellError> {
|
||||
Err(ShellError::GenericError {
|
||||
error: msg.into(),
|
||||
msg: "".into(),
|
||||
span: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
let path = path.as_ref();
|
||||
|
||||
if !path.is_absolute() {
|
||||
error("Cannot set $env.PWD to a non-absolute path")
|
||||
} else if !path.exists() {
|
||||
error("Cannot set $env.PWD to a non-existent directory")
|
||||
} else if !path.is_dir() {
|
||||
error("Cannot set $env.PWD to a non-directory")
|
||||
} else {
|
||||
// Strip trailing slashes, if any.
|
||||
let path = nu_path::strip_trailing_slash(path);
|
||||
let value = Value::string(path.to_string_lossy(), Span::unknown());
|
||||
self.add_env_var("PWD".into(), value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Reference in New Issue
Block a user