mirror of
https://github.com/nushell/nushell
synced 2025-01-27 12:25:19 +00:00
Refactor to create EnvMaintainer trait for interact with Stack/EngineState. 'ls' now PWD-per-drive ready.
This commit is contained in:
parent
2b9f6e64a1
commit
8c59cbf913
4 changed files with 286 additions and 263 deletions
|
@ -4,8 +4,8 @@ use nu_engine::glob_from;
|
|||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, env::current_dir};
|
||||
use nu_glob::MatchOptions;
|
||||
use nu_path::{expand_path_with, expand_to_real_path};
|
||||
use nu_protocol::{DataSource, NuGlob, PipelineMetadata, Signals};
|
||||
use nu_path::expand_to_real_path;
|
||||
use nu_protocol::{engine::expand_path_with, DataSource, NuGlob, PipelineMetadata};
|
||||
use pathdiff::diff_paths;
|
||||
use rayon::prelude::*;
|
||||
|
||||
|
@ -98,8 +98,6 @@ impl Command for Ls {
|
|||
let use_mime_type = call.has_flag(engine_state, stack, "mime-type")?;
|
||||
let use_threads = call.has_flag(engine_state, stack, "threads")?;
|
||||
let call_span = call.head;
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
let args = Args {
|
||||
all,
|
||||
|
@ -120,26 +118,19 @@ impl Command for Ls {
|
|||
Some(pattern_arg)
|
||||
};
|
||||
match input_pattern_arg {
|
||||
None => Ok(
|
||||
ls_for_one_pattern(None, args, engine_state.signals().clone(), cwd)?
|
||||
.into_pipeline_data_with_metadata(
|
||||
call_span,
|
||||
engine_state.signals().clone(),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Ls,
|
||||
content_type: None,
|
||||
},
|
||||
),
|
||||
),
|
||||
None => Ok(ls_for_one_pattern(None, args, engine_state, stack)?
|
||||
.into_pipeline_data_with_metadata(
|
||||
call_span,
|
||||
engine_state.signals().clone(),
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::Ls,
|
||||
content_type: None,
|
||||
},
|
||||
)),
|
||||
Some(pattern) => {
|
||||
let mut result_iters = vec![];
|
||||
for pat in pattern {
|
||||
result_iters.push(ls_for_one_pattern(
|
||||
Some(pat),
|
||||
args,
|
||||
engine_state.signals().clone(),
|
||||
cwd.clone(),
|
||||
)?)
|
||||
result_iters.push(ls_for_one_pattern(Some(pat), args, engine_state, stack)?)
|
||||
}
|
||||
|
||||
// Here nushell needs to use
|
||||
|
@ -221,8 +212,8 @@ impl Command for Ls {
|
|||
fn ls_for_one_pattern(
|
||||
pattern_arg: Option<Spanned<NuGlob>>,
|
||||
args: Args,
|
||||
signals: Signals,
|
||||
cwd: PathBuf,
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
fn create_pool(num_threads: usize) -> Result<rayon::ThreadPool, ShellError> {
|
||||
match rayon::ThreadPoolBuilder::new()
|
||||
|
@ -240,6 +231,10 @@ fn ls_for_one_pattern(
|
|||
}
|
||||
}
|
||||
|
||||
let signals = engine_state.signals().clone();
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let Args {
|
||||
|
@ -282,8 +277,13 @@ fn ls_for_one_pattern(
|
|||
let (pattern_arg, absolute_path) = match pattern_arg {
|
||||
Some(pat) => {
|
||||
// expand with cwd here is only used for checking
|
||||
let tmp_expanded =
|
||||
nu_path::expand_path_with(pat.item.as_ref(), &cwd, pat.item.is_expand());
|
||||
let tmp_expanded = expand_path_with(
|
||||
stack,
|
||||
engine_state,
|
||||
pat.item.as_ref(),
|
||||
&cwd,
|
||||
pat.item.is_expand(),
|
||||
);
|
||||
// Avoid checking and pushing "*" to the path when directory (do not show contents) flag is true
|
||||
if !directory && tmp_expanded.is_dir() {
|
||||
if read_dir(&tmp_expanded, p_tag, use_threads)?
|
||||
|
@ -319,7 +319,13 @@ fn ls_for_one_pattern(
|
|||
let hidden_dir_specified = is_hidden_dir(pattern_arg.as_ref());
|
||||
let path = pattern_arg.into_spanned(p_tag);
|
||||
let (prefix, paths) = if just_read_dir {
|
||||
let expanded = nu_path::expand_path_with(path.item.as_ref(), &cwd, path.item.is_expand());
|
||||
let expanded = expand_path_with(
|
||||
stack,
|
||||
engine_state,
|
||||
path.item.as_ref(),
|
||||
&cwd,
|
||||
path.item.is_expand(),
|
||||
);
|
||||
let paths = read_dir(&expanded, p_tag, use_threads)?;
|
||||
// just need to read the directory, so prefix is path itself.
|
||||
(Some(expanded), paths)
|
||||
|
@ -349,8 +355,6 @@ fn ls_for_one_pattern(
|
|||
|
||||
let hidden_dirs = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let signals_clone = signals.clone();
|
||||
|
||||
let pool = if use_threads {
|
||||
let count = std::thread::available_parallelism()?.get();
|
||||
create_pool(count)?
|
||||
|
@ -436,7 +440,8 @@ fn ls_for_one_pattern(
|
|||
call_span,
|
||||
long,
|
||||
du,
|
||||
&signals_clone,
|
||||
engine_state,
|
||||
stack,
|
||||
use_mime_type,
|
||||
args.full_paths,
|
||||
);
|
||||
|
@ -554,7 +559,8 @@ pub(crate) fn dir_entry_dict(
|
|||
span: Span,
|
||||
long: bool,
|
||||
du: bool,
|
||||
signals: &Signals,
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
use_mime_type: bool,
|
||||
full_symlink_target: bool,
|
||||
) -> Result<Value, ShellError> {
|
||||
|
@ -568,6 +574,8 @@ pub(crate) fn dir_entry_dict(
|
|||
));
|
||||
}
|
||||
|
||||
let signals = &engine_state.signals().clone();
|
||||
|
||||
let mut record = Record::new();
|
||||
let mut file_type = "unknown".to_string();
|
||||
|
||||
|
@ -591,6 +599,8 @@ pub(crate) fn dir_entry_dict(
|
|||
if full_symlink_target && filename.parent().is_some() {
|
||||
Value::string(
|
||||
expand_path_with(
|
||||
stack,
|
||||
engine_state,
|
||||
path_to_link,
|
||||
filename
|
||||
.parent()
|
||||
|
|
|
@ -29,7 +29,9 @@ pub use engine_state::*;
|
|||
pub use error_handler::*;
|
||||
pub use overlay::*;
|
||||
pub use pattern_match::*;
|
||||
pub use pwd_per_drive::*;
|
||||
pub use pwd_per_drive::expand_path_with;
|
||||
#[cfg(windows)]
|
||||
pub use pwd_per_drive::windows::{expand_pwd, set_pwd};
|
||||
pub use sequence::*;
|
||||
pub use stack::*;
|
||||
pub use stack_out_dest::*;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use crate::engine::{EngineState, Stack};
|
||||
#[cfg(windows)]
|
||||
use crate::{Span, Value};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// For file system command usage
|
||||
|
@ -26,256 +24,266 @@ where
|
|||
Q: AsRef<Path>,
|
||||
{
|
||||
#[cfg(windows)]
|
||||
if let Some(abs_path) = expand_pwd(_stack, _engine_state, path.as_ref()) {
|
||||
if let Some(abs_path) = windows::expand_pwd(_stack, _engine_state, path.as_ref()) {
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
nu_path::expand_path_with::<P, Q>(path, relative_to, expand_tilde)
|
||||
}
|
||||
|
||||
/// For maintainer to notify current pwd
|
||||
/// When user change current directory, maintainer notifies
|
||||
/// PWD-per-drive by calling set_pwd() with current stack and path;
|
||||
#[cfg(windows)]
|
||||
pub fn set_pwd(stack: &mut Stack, path: &Path) {
|
||||
if let Some(path_str) = path.to_str() {
|
||||
if let Some(drive) = extract_drive_letter(path_str) {
|
||||
stack.add_env_var(
|
||||
env_var_for_drive(drive),
|
||||
Value::string(path_str, Span::unknown()).clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For file system command usage
|
||||
/// File system command implementation can also directly use expand_pwd
|
||||
/// to expand relate path for a drive and strip redundant double or
|
||||
/// single quote like bash.
|
||||
/// cd "''C:''nushell''"
|
||||
/// C:\Users\nushell>
|
||||
#[cfg(windows)]
|
||||
pub fn expand_pwd(stack: &Stack, engine_state: &EngineState, path: &Path) -> Option<PathBuf> {
|
||||
if let Some(path_str) = path.to_str() {
|
||||
if let Some(drive_letter) = need_expand(path_str) {
|
||||
let mut base = PathBuf::from(get_pwd_on_drive(stack, engine_state, drive_letter));
|
||||
// need_expand() ensures path_str.len() >= 2
|
||||
base.push(&path_str[2..]); // Join PWD with path parts after "C:"
|
||||
return Some(base);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Implementation for maintainer and fs_client
|
||||
/// Windows env var for drive
|
||||
/// essential for integration with windows native shell CMD/PowerShell
|
||||
/// and the core mechanism for supporting PWD-per-drive with nushell's
|
||||
/// powerful layered environment system.
|
||||
/// Returns uppercased "=X:".
|
||||
#[cfg(windows)]
|
||||
pub fn env_var_for_drive(drive_letter: char) -> String {
|
||||
let drive_letter = drive_letter.to_ascii_uppercase();
|
||||
format!("={}:", drive_letter)
|
||||
}
|
||||
|
||||
/// get pwd for drive:
|
||||
/// 1. From env_var, if no,
|
||||
/// 2. From sys_absolute, if no,
|
||||
/// 3. Construct root path to drives
|
||||
#[cfg(windows)]
|
||||
pub fn get_pwd_on_drive(stack: &Stack, engine_state: &EngineState, drive_letter: char) -> String {
|
||||
let env_var_for_drive = env_var_for_drive(drive_letter);
|
||||
let mut abs_pwd: Option<String> = None;
|
||||
if let Some(pwd) = stack.get_env_var(engine_state, &env_var_for_drive) {
|
||||
if let Ok(pwd_string) = pwd.clone().into_string() {
|
||||
abs_pwd = Some(pwd_string);
|
||||
}
|
||||
}
|
||||
if abs_pwd.is_none() {
|
||||
if let Some(sys_pwd) = get_full_path_name_w(&format!("{}:", drive_letter)) {
|
||||
abs_pwd = Some(sys_pwd);
|
||||
}
|
||||
}
|
||||
if let Some(pwd) = abs_pwd {
|
||||
ensure_trailing_delimiter(&pwd)
|
||||
} else {
|
||||
format!(r"{}:\", drive_letter)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if input path is relative path for drive letter,
|
||||
/// which should be expanded with PWD-per-drive.
|
||||
/// Returns Some(drive_letter) or None, drive_letter is upper case.
|
||||
#[cfg(windows)]
|
||||
pub fn need_expand(path: &str) -> Option<char> {
|
||||
let chars: Vec<char> = path.chars().collect();
|
||||
if chars.len() == 2 || (chars.len() > 2 && chars[2] != '/' && chars[2] != '\\') {
|
||||
extract_drive_letter(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the drive letter from a path, return uppercased
|
||||
/// drive letter or None
|
||||
#[cfg(windows)]
|
||||
pub fn extract_drive_letter(path: &str) -> Option<char> {
|
||||
let chars: Vec<char> = path.chars().collect();
|
||||
if chars.len() >= 2 && chars[0].is_ascii_alphabetic() && chars[1] == ':' {
|
||||
Some(chars[0].to_ascii_uppercase())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure a path has a trailing `\\` or '/'
|
||||
#[cfg(windows)]
|
||||
pub fn ensure_trailing_delimiter(path: &str) -> String {
|
||||
if !path.ends_with('\\') && !path.ends_with('/') {
|
||||
format!(r"{}\", path)
|
||||
} else {
|
||||
path.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// get_full_path_name_w
|
||||
/// Call Windows system API (via omnipath crate) to expand
|
||||
/// absolute path
|
||||
#[cfg(windows)]
|
||||
pub fn get_full_path_name_w(path_str: &str) -> Option<String> {
|
||||
use omnipath::sys_absolute;
|
||||
use std::path::Path;
|
||||
|
||||
if let Ok(path_sys_abs) = sys_absolute(Path::new(path_str)) {
|
||||
Some(path_sys_abs.to_str()?.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(windows, test))] // test only for windows
|
||||
mod tests {
|
||||
pub mod windows {
|
||||
use super::*;
|
||||
use crate::{FromValue, Value};
|
||||
|
||||
#[test]
|
||||
fn test_expand_path_with() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
let path = Path::new(path_str);
|
||||
set_pwd(&mut stack, path);
|
||||
let engine_state = EngineState::new();
|
||||
|
||||
let rel_path = Path::new("c:.config");
|
||||
let result = format!(r"{path_str}\.config");
|
||||
assert_eq!(
|
||||
Some(result.as_str()),
|
||||
expand_path_with(&stack, &engine_state, rel_path, Path::new(path_str), false)
|
||||
.as_path()
|
||||
.to_str()
|
||||
);
|
||||
pub trait EnvMaintainer {
|
||||
fn maintain(&mut self, key: String, value: Value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_pwd() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
let path = Path::new(path_str);
|
||||
set_pwd(&mut stack, path);
|
||||
let engine_state = EngineState::new();
|
||||
assert_eq!(
|
||||
stack
|
||||
.get_env_var(&engine_state, &env_var_for_drive('c'))
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into_string()
|
||||
.unwrap(),
|
||||
path_str.to_string()
|
||||
);
|
||||
macro_rules! impl_env_maintainer {
|
||||
($type:ty) => {
|
||||
impl EnvMaintainer for $type {
|
||||
fn maintain(&mut self, key: String, value: Value) {
|
||||
self.add_env_var(key, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_pwd() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
let path = Path::new(path_str);
|
||||
set_pwd(&mut stack, path);
|
||||
let engine_state = EngineState::new();
|
||||
impl_env_maintainer!(Stack);
|
||||
impl_env_maintainer!(EngineState);
|
||||
|
||||
let rel_path = Path::new("c:.config");
|
||||
let result = format!(r"{path_str}\.config");
|
||||
assert_eq!(
|
||||
Some(result.as_str()),
|
||||
expand_pwd(&stack, &engine_state, rel_path)
|
||||
.unwrap()
|
||||
.as_path()
|
||||
.to_str()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var_for_drive() {
|
||||
for drive_letter in 'A'..='Z' {
|
||||
assert_eq!(env_var_for_drive(drive_letter), format!("={drive_letter}:"));
|
||||
/// For maintainer to notify PWD
|
||||
/// When user changes current directory, maintainer calls set_pwd()
|
||||
/// and PWD-per-drive call maintainer back to store it for the
|
||||
/// env_var_for_drive.
|
||||
pub fn set_pwd<T: EnvMaintainer>(maintainer: &mut T, value: Value) {
|
||||
if let Ok(path_string) = String::from_value(value.clone()) {
|
||||
if let Some(drive) = extract_drive_letter(&path_string) {
|
||||
maintainer.maintain(env_var_for_drive(drive), value);
|
||||
}
|
||||
}
|
||||
for drive_letter in 'a'..='z' {
|
||||
}
|
||||
|
||||
/// For file system command usage
|
||||
/// File system command implementation can also directly use expand_pwd
|
||||
/// to expand relate path for a drive and strip redundant double or
|
||||
/// single quote like bash.
|
||||
/// cd "''C:''nushell''"
|
||||
/// C:\Users\nushell>
|
||||
pub fn expand_pwd(stack: &Stack, engine_state: &EngineState, path: &Path) -> Option<PathBuf> {
|
||||
if let Some(path_str) = path.to_str() {
|
||||
if let Some(drive_letter) = need_expand(path_str) {
|
||||
let mut base = PathBuf::from(get_pwd_on_drive(stack, engine_state, drive_letter));
|
||||
// need_expand() ensures path_str.len() >= 2
|
||||
base.push(&path_str[2..]); // Join PWD with path parts after "C:"
|
||||
return Some(base);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Implementation for maintainer and fs_client
|
||||
/// Windows env var for drive
|
||||
/// essential for integration with windows native shell CMD/PowerShell
|
||||
/// and the core mechanism for supporting PWD-per-drive with nushell's
|
||||
/// powerful layered environment system.
|
||||
/// Returns uppercased "=X:".
|
||||
fn env_var_for_drive(drive_letter: char) -> String {
|
||||
let drive_letter = drive_letter.to_ascii_uppercase();
|
||||
format!("={}:", drive_letter)
|
||||
}
|
||||
|
||||
/// get pwd for drive:
|
||||
/// 1. From env_var, if no,
|
||||
/// 2. From sys_absolute, if no,
|
||||
/// 3. Construct root path to drives
|
||||
fn get_pwd_on_drive(stack: &Stack, engine_state: &EngineState, drive_letter: char) -> String {
|
||||
let env_var_for_drive = env_var_for_drive(drive_letter);
|
||||
let mut abs_pwd: Option<String> = None;
|
||||
if let Some(pwd) = stack.get_env_var(engine_state, &env_var_for_drive) {
|
||||
if let Ok(pwd_string) = pwd.clone().into_string() {
|
||||
abs_pwd = Some(pwd_string);
|
||||
}
|
||||
}
|
||||
if abs_pwd.is_none() {
|
||||
if let Some(sys_pwd) = get_full_path_name_w(&format!("{}:", drive_letter)) {
|
||||
abs_pwd = Some(sys_pwd);
|
||||
}
|
||||
}
|
||||
if let Some(pwd) = abs_pwd {
|
||||
ensure_trailing_delimiter(&pwd)
|
||||
} else {
|
||||
format!(r"{}:\", drive_letter)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if input path is relative path for drive letter,
|
||||
/// which should be expanded with PWD-per-drive.
|
||||
/// Returns Some(drive_letter) or None, drive_letter is upper case.
|
||||
fn need_expand(path: &str) -> Option<char> {
|
||||
let chars: Vec<char> = path.chars().collect();
|
||||
if chars.len() == 2 || (chars.len() > 2 && chars[2] != '/' && chars[2] != '\\') {
|
||||
extract_drive_letter(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the drive letter from a path, return uppercased
|
||||
/// drive letter or None
|
||||
fn extract_drive_letter(path: &str) -> Option<char> {
|
||||
let chars: Vec<char> = path.chars().collect();
|
||||
if chars.len() >= 2 && chars[0].is_ascii_alphabetic() && chars[1] == ':' {
|
||||
Some(chars[0].to_ascii_uppercase())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure a path has a trailing `\\` or '/'
|
||||
fn ensure_trailing_delimiter(path: &str) -> String {
|
||||
if !path.ends_with('\\') && !path.ends_with('/') {
|
||||
format!(r"{}\", path)
|
||||
} else {
|
||||
path.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// get_full_path_name_w
|
||||
/// Call Windows system API (via omnipath crate) to expand
|
||||
/// absolute path
|
||||
fn get_full_path_name_w(path_str: &str) -> Option<String> {
|
||||
use omnipath::sys_absolute;
|
||||
use std::path::Path;
|
||||
|
||||
if let Ok(path_sys_abs) = sys_absolute(Path::new(path_str)) {
|
||||
Some(path_sys_abs.to_str()?.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)] // test only for windows
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{IntoValue, Span};
|
||||
|
||||
#[test]
|
||||
fn test_expand_path_with() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
set_pwd(&mut stack, path_str.into_value(Span::unknown()));
|
||||
let engine_state = EngineState::new();
|
||||
|
||||
let rel_path = Path::new("c:.config");
|
||||
let result = format!(r"{path_str}\.config");
|
||||
assert_eq!(
|
||||
env_var_for_drive(drive_letter),
|
||||
format!("={}:", drive_letter.to_ascii_uppercase())
|
||||
Some(result.as_str()),
|
||||
expand_path_with(&stack, &engine_state, rel_path, Path::new(path_str), false)
|
||||
.as_path()
|
||||
.to_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_pwd_on_drive() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
let path = Path::new(path_str);
|
||||
set_pwd(&mut stack, path);
|
||||
let engine_state = EngineState::new();
|
||||
let result = format!(r"{path_str}\");
|
||||
assert_eq!(result, get_pwd_on_drive(&stack, &engine_state, 'c'));
|
||||
}
|
||||
#[test]
|
||||
fn test_set_pwd() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
set_pwd(&mut stack, path_str.into_value(Span::unknown()));
|
||||
let engine_state = EngineState::new();
|
||||
assert_eq!(
|
||||
stack
|
||||
.get_env_var(&engine_state, &env_var_for_drive('c'))
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into_string()
|
||||
.unwrap(),
|
||||
path_str.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_need_expand() {
|
||||
assert_eq!(need_expand(r"c:nushell\src"), Some('C'));
|
||||
assert_eq!(need_expand("C:src/"), Some('C'));
|
||||
assert_eq!(need_expand("a:"), Some('A'));
|
||||
assert_eq!(need_expand("z:"), Some('Z'));
|
||||
// Absolute path does not need expand
|
||||
assert_eq!(need_expand(r"c:\"), None);
|
||||
// Unix path does not need expand
|
||||
assert_eq!(need_expand("/usr/bin"), None);
|
||||
// Invalid path on drive
|
||||
assert_eq!(need_expand("1:usr/bin"), None);
|
||||
}
|
||||
#[test]
|
||||
fn test_expand_pwd() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
set_pwd(&mut stack, path_str.into_value(Span::unknown()));
|
||||
let engine_state = EngineState::new();
|
||||
|
||||
#[test]
|
||||
fn test_extract_drive_letter() {
|
||||
assert_eq!(extract_drive_letter("C:test"), Some('C'));
|
||||
assert_eq!(extract_drive_letter(r"d:\temp"), Some('D'));
|
||||
assert_eq!(extract_drive_letter(r"1:temp"), None);
|
||||
}
|
||||
let rel_path = Path::new("c:.config");
|
||||
let result = format!(r"{path_str}\.config");
|
||||
assert_eq!(
|
||||
Some(result.as_str()),
|
||||
expand_pwd(&stack, &engine_state, rel_path)
|
||||
.unwrap()
|
||||
.as_path()
|
||||
.to_str()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_trailing_delimiter() {
|
||||
assert_eq!(ensure_trailing_delimiter("E:"), r"E:\");
|
||||
assert_eq!(ensure_trailing_delimiter(r"e:\"), r"e:\");
|
||||
assert_eq!(ensure_trailing_delimiter("c:/"), "c:/");
|
||||
}
|
||||
#[test]
|
||||
fn test_env_var_for_drive() {
|
||||
for drive_letter in 'A'..='Z' {
|
||||
assert_eq!(env_var_for_drive(drive_letter), format!("={drive_letter}:"));
|
||||
}
|
||||
for drive_letter in 'a'..='z' {
|
||||
assert_eq!(
|
||||
env_var_for_drive(drive_letter),
|
||||
format!("={}:", drive_letter.to_ascii_uppercase())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_full_path_name_w() {
|
||||
let result = get_full_path_name_w("C:");
|
||||
assert!(result.is_some());
|
||||
let path = result.unwrap();
|
||||
assert!(path.starts_with(r"C:\"));
|
||||
#[test]
|
||||
fn test_get_pwd_on_drive() {
|
||||
let mut stack = Stack::new();
|
||||
let path_str = r"c:\users\nushell";
|
||||
set_pwd(&mut stack, path_str.into_value(Span::unknown()));
|
||||
let engine_state = EngineState::new();
|
||||
let result = format!(r"{path_str}\");
|
||||
assert_eq!(result, get_pwd_on_drive(&stack, &engine_state, 'c'));
|
||||
}
|
||||
|
||||
let result = get_full_path_name_w(r"c:nushell\src");
|
||||
assert!(result.is_some());
|
||||
let path = result.unwrap();
|
||||
assert!(path.starts_with(r"C:\") || path.starts_with(r"c:\"));
|
||||
assert!(path.ends_with(r"nushell\src"));
|
||||
#[test]
|
||||
fn test_need_expand() {
|
||||
assert_eq!(need_expand(r"c:nushell\src"), Some('C'));
|
||||
assert_eq!(need_expand("C:src/"), Some('C'));
|
||||
assert_eq!(need_expand("a:"), Some('A'));
|
||||
assert_eq!(need_expand("z:"), Some('Z'));
|
||||
// Absolute path does not need expand
|
||||
assert_eq!(need_expand(r"c:\"), None);
|
||||
// Unix path does not need expand
|
||||
assert_eq!(need_expand("/usr/bin"), None);
|
||||
// Invalid path on drive
|
||||
assert_eq!(need_expand("1:usr/bin"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_drive_letter() {
|
||||
assert_eq!(extract_drive_letter("C:test"), Some('C'));
|
||||
assert_eq!(extract_drive_letter(r"d:\temp"), Some('D'));
|
||||
assert_eq!(extract_drive_letter(r"1:temp"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_trailing_delimiter() {
|
||||
assert_eq!(ensure_trailing_delimiter("E:"), r"E:\");
|
||||
assert_eq!(ensure_trailing_delimiter(r"e:\"), r"e:\");
|
||||
assert_eq!(ensure_trailing_delimiter("c:/"), "c:/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_full_path_name_w() {
|
||||
let result = get_full_path_name_w("C:");
|
||||
assert!(result.is_some());
|
||||
let path = result.unwrap();
|
||||
assert!(path.starts_with(r"C:\"));
|
||||
|
||||
let result = get_full_path_name_w(r"c:nushell\src");
|
||||
assert!(result.is_some());
|
||||
let path = result.unwrap();
|
||||
assert!(path.starts_with(r"C:\") || path.starts_with(r"c:\"));
|
||||
assert!(path.ends_with(r"nushell\src"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#[cfg(windows)]
|
||||
use crate::engine::pwd_per_drive;
|
||||
use crate::engine::set_pwd;
|
||||
use crate::{
|
||||
engine::{
|
||||
ArgumentStack, EngineState, ErrorHandlerStack, Redirection, StackCallArgGuard,
|
||||
|
@ -253,6 +253,11 @@ impl Stack {
|
|||
}
|
||||
|
||||
pub fn add_env_var(&mut self, var: String, value: Value) {
|
||||
#[cfg(windows)]
|
||||
if var == "PWD" {
|
||||
set_pwd(self, value.clone());
|
||||
}
|
||||
|
||||
if let Some(last_overlay) = self.active_overlays.last() {
|
||||
if let Some(env_hidden) = Arc::make_mut(&mut self.env_hidden).get_mut(last_overlay) {
|
||||
// if the env var was hidden, let's activate it again
|
||||
|
@ -763,8 +768,6 @@ impl Stack {
|
|||
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);
|
||||
#[cfg(windows)] // Sync with PWD-per-drive
|
||||
pwd_per_drive::set_pwd(self, &path);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue