Files
nushell/crates/nu-std/src/lib.rs
YizhePKU bdb6daa4b5 Migrate to a new PWD API (#12603)
This is the first PR towards migrating to a new `$env.PWD` API that
returns potentially un-canonicalized paths. Refer to PR #12515 for
motivations.

## New API: `EngineState::cwd()`

The goal of the new API is to cover both parse-time and runtime use
case, and avoid unintentional misuse. It takes an `Option<Stack>` as
argument, which if supplied, will search for `$env.PWD` on the stack in
additional to the engine state. I think with this design, there's less
confusion over parse-time and runtime environments. If you have access
to a stack, just supply it; otherwise supply `None`.

## Deprecation of other PWD-related APIs

Other APIs are re-implemented using `EngineState::cwd()` and properly
documented. They're marked deprecated, but their behavior is unchanged.
Unused APIs are deleted, and code that accesses `$env.PWD` directly
without using an API is rewritten.

Deprecated APIs:

* `EngineState::current_work_dir()`
* `StateWorkingSet::get_cwd()`
* `env::current_dir()`
* `env::current_dir_str()`
* `env::current_dir_const()`
* `env::current_dir_str_const()`

Other changes:

* `EngineState::get_cwd()` (deleted)
* `StateWorkingSet::list_env()` (deleted)
* `repl::do_run_cmd()` (rewritten with `env::current_dir_str()`)

## `cd` and `pwd` now use logical paths by default

This pulls the changes from PR #12515. It's currently somewhat broken
because using non-canonicalized paths exposed a bug in our path
normalization logic (Issue #12602). Once that is fixed, this should
work.

## Future plans

This PR needs some tests. Which test helpers should I use, and where
should I put those tests?

I noticed that unquoted paths are expanded within `eval_filepath()` and
`eval_directory()` before they even reach the `cd` command. This means
every paths is expanded twice. Is this intended?

Once this PR lands, the plan is to review all usages of the deprecated
APIs and migrate them to `EngineState::cwd()`. In the meantime, these
usages are annotated with `#[allow(deprecated)]` to avoid breaking CI.

---------

Co-authored-by: Jakub Žádník <kubouch@gmail.com>
2024-05-03 14:33:09 +03:00

108 lines
3.5 KiB
Rust

use log::trace;
#[allow(deprecated)]
use nu_engine::{env::current_dir, eval_block};
use nu_parser::parse;
use nu_protocol::{
debugger::WithoutDebug,
engine::{FileStack, Stack, StateWorkingSet, VirtualPath},
report_error, PipelineData,
};
use std::path::PathBuf;
// Virtual std directory unlikely to appear in user's file system
const NU_STDLIB_VIRTUAL_DIR: &str = "NU_STDLIB_VIRTUAL_DIR";
pub fn load_standard_library(
engine_state: &mut nu_protocol::engine::EngineState,
) -> Result<(), miette::ErrReport> {
trace!("load_standard_library");
let (block, delta) = {
// Using full virtual path to avoid potential conflicts with user having 'std' directory
// in their working directory.
let std_dir = PathBuf::from(NU_STDLIB_VIRTUAL_DIR).join("std");
let mut std_files = vec![
("mod.nu", include_str!("../std/mod.nu")),
("dirs.nu", include_str!("../std/dirs.nu")),
("dt.nu", include_str!("../std/dt.nu")),
("help.nu", include_str!("../std/help.nu")),
("iter.nu", include_str!("../std/iter.nu")),
("log.nu", include_str!("../std/log.nu")),
("assert.nu", include_str!("../std/assert.nu")),
("xml.nu", include_str!("../std/xml.nu")),
("input.nu", include_str!("../std/input.nu")),
("math.nu", include_str!("../std/math.nu")),
("formats.nu", include_str!("../std/formats.nu")),
];
let mut working_set = StateWorkingSet::new(engine_state);
let mut std_virt_paths = vec![];
for (name, content) in std_files.drain(..) {
let name = std_dir.join(name);
let file_id =
working_set.add_file(name.to_string_lossy().to_string(), content.as_bytes());
let virtual_file_id = working_set.add_virtual_path(
name.to_string_lossy().to_string(),
VirtualPath::File(file_id),
);
std_virt_paths.push(virtual_file_id);
}
let std_dir = std_dir.to_string_lossy().to_string();
let source = r#"
# Define the `std` module
module std
# Prelude
use std dirs [
enter
shells
g
n
p
dexit
]
use std pwd
"#;
let _ = working_set.add_virtual_path(std_dir, VirtualPath::Dir(std_virt_paths));
// Add a placeholder file to the stack of files being evaluated.
// The name of this file doesn't matter; it's only there to set the current working directory to NU_STDLIB_VIRTUAL_DIR.
let placeholder = PathBuf::from(NU_STDLIB_VIRTUAL_DIR).join("loading stdlib");
working_set.files = FileStack::with_file(placeholder);
let block = parse(
&mut working_set,
Some("loading stdlib"),
source.as_bytes(),
false,
);
// Remove the placeholder file from the stack of files being evaluated.
working_set.files.pop();
if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err);
}
(block, working_set.render())
};
engine_state.merge_delta(delta)?;
// We need to evaluate the module in order to run the `export-env` blocks.
let mut stack = Stack::new();
let pipeline_data = PipelineData::Empty;
eval_block::<WithoutDebug>(engine_state, &mut stack, &block, pipeline_data)?;
#[allow(deprecated)]
let cwd = current_dir(engine_state, &stack)?;
engine_state.merge_env(&mut stack, cwd)?;
Ok(())
}