mirror of
https://github.com/nushell/nushell.git
synced 2025-01-08 15:30:24 +01:00
Remove index
This commit is contained in:
parent
f22f2985a7
commit
a6f95c282c
@ -1,43 +1,40 @@
|
|||||||
cfg_if::cfg_if! {
|
use std::path::{Path, PathBuf};
|
||||||
if #[cfg(target_os="windows")] {
|
|
||||||
|
cfg_if::cfg_if! { if #[cfg(windows)] {
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::path::{ Path, PathBuf };
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
struct DrivePWDmap {
|
struct Drive2PWDmap {
|
||||||
map: [Option<String>; 26], // Fixed-size array for A-Z
|
map: [Option<String>; 26], // Fixed-size array for A-Z
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrivePWDmap {
|
impl Drive2PWDmap {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
DrivePWDmap {
|
Drive2PWDmap {
|
||||||
map: Default::default(), // Initialize all to `None`
|
map: Default::default(), // Initialize all to `None`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the PWD for the drive letter in the path which is an absolute path
|
/// Set the PWD for the drive letter in the absolute path.
|
||||||
|
/// Return String for error description.
|
||||||
pub fn set_pwd(&mut self, path: &Path) -> Result<(), String> {
|
pub fn set_pwd(&mut self, path: &Path) -> Result<(), String> {
|
||||||
if let Some(drive_letter) = Self::extract_drive_letter(path) {
|
if let (Some(drive_letter), Some(path_str)) = (
|
||||||
if let Some(index) = Self::drive_to_index(drive_letter) {
|
Self::extract_drive_letter(path),
|
||||||
if let Some(path_str) = path.to_str() {
|
path.to_str(),
|
||||||
self.map[index] = Some(path_str.to_string());
|
) {
|
||||||
Ok(())
|
self.map[drive_letter as usize - 'A' as usize] = Some(path_str.to_string());
|
||||||
} else {
|
return Ok(());
|
||||||
|
}
|
||||||
Err(format!("Invalid path: {}", path.display()))
|
Err(format!("Invalid path: {}", path.display()))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Err(format!("Invalid drive letter: {}", drive_letter))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(format!("Invalid path: {}", path.display()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the PWD for a drive letter, if not yet, try using
|
/// Get the PWD for drive, if not yet, ask GetFullPathNameW,
|
||||||
/// winapi GetFullPathNameW to get "T:", "T:/" can be default
|
/// or else return default "X:/".
|
||||||
pub fn get_pwd(&mut self, drive: char) -> Option<String> {
|
fn get_pwd(&mut self, drive: char) -> Option<String> {
|
||||||
Self::drive_to_index(drive).map(|index| {
|
if drive.is_ascii_alphabetic() && drive.is_ascii_uppercase() {
|
||||||
|
let index = drive as usize - 'A' as usize;
|
||||||
|
Some(
|
||||||
self.map[index]
|
self.map[index]
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(||
|
.unwrap_or_else(||
|
||||||
@ -48,10 +45,15 @@ impl DrivePWDmap {
|
|||||||
format!("{}:/", drive.to_ascii_uppercase())
|
format!("{}:/", drive.to_ascii_uppercase())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expand a relative path using the PWD of the drive
|
/// Expand a relative path using the PWD-per-drive, return PathBuf
|
||||||
|
/// of absolute path.
|
||||||
|
/// Return None if path is not valid or can't get drive letter.
|
||||||
pub fn expand_path(&mut self, path: &Path) -> Option<PathBuf> {
|
pub fn expand_path(&mut self, path: &Path) -> Option<PathBuf> {
|
||||||
let path_str = path.to_str()?;
|
let path_str = path.to_str()?;
|
||||||
if let Some(drive_letter) = Self::extract_drive_letter(path) {
|
if let Some(drive_letter) = Self::extract_drive_letter(path) {
|
||||||
@ -59,30 +61,29 @@ impl DrivePWDmap {
|
|||||||
// Combine current PWD with the relative path
|
// Combine current PWD with the relative path
|
||||||
let mut base = PathBuf::from(Self::ensure_trailing_separator(&pwd));
|
let mut base = PathBuf::from(Self::ensure_trailing_separator(&pwd));
|
||||||
base.push(path_str.split_at(2).1); // Skip the "C:" part of the relative path
|
base.push(path_str.split_at(2).1); // Skip the "C:" part of the relative path
|
||||||
Some(base)
|
return Some(base)
|
||||||
} else {
|
|
||||||
None // PWD on Drive letter not found
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
None // Invalid or no drive letter
|
|
||||||
}
|
}
|
||||||
|
None // Invalid path or has no drive letter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to convert a drive letter to an array index
|
/// Helper to convert a drive letter to an array index
|
||||||
fn drive_to_index(drive: char) -> Option<usize> {
|
// fn drive_to_index(drive: char) -> Option<usize> {
|
||||||
let drive = drive.to_ascii_uppercase();
|
// let drive = drive.to_ascii_uppercase();
|
||||||
if drive.is_ascii_uppercase() {
|
// if drive.is_ascii_uppercase() {
|
||||||
Some((drive as usize) - ('A' as usize))
|
// Some((drive as usize) - ('A' as usize))
|
||||||
} else {
|
// } else {
|
||||||
None
|
// None
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Extract the drive letter from a path (e.g., `C:test` -> `C`)
|
/// Extract the drive letter from a path (e.g., `C:test` -> `C`)
|
||||||
fn extract_drive_letter(path: &Path) -> Option<char> {
|
fn extract_drive_letter(path: &Path) -> Option<char> {
|
||||||
path.to_str()
|
Some(path.to_str()
|
||||||
.and_then(|s| s.chars().next())
|
.and_then(|s| s.chars().next())
|
||||||
.filter(|c| c.is_ascii_alphabetic())
|
.filter(|c| c.is_ascii_alphabetic())?
|
||||||
|
.to_ascii_uppercase()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure a path has a trailing `\`
|
/// Ensure a path has a trailing `\`
|
||||||
@ -101,10 +102,9 @@ fn get_full_path_name_w(path_str: &str) -> Option<String> {
|
|||||||
use std::os::windows::ffi::OsStringExt;
|
use std::os::windows::ffi::OsStringExt;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
use winapi::um::fileapi::GetFullPathNameW;
|
use winapi::um::fileapi::GetFullPathNameW;
|
||||||
use winapi::um::winnt::WCHAR;
|
|
||||||
|
|
||||||
const MAX_PATH : usize = 260;
|
const MAX_PATH : usize = 260;
|
||||||
let mut buffer: [WCHAR; MAX_PATH] = [0; MAX_PATH];
|
let mut buffer: [u16; MAX_PATH] = [0; MAX_PATH];
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Convert input to wide string.
|
// Convert input to wide string.
|
||||||
@ -130,10 +130,10 @@ fn get_full_path_name_w(path_str: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Global singleton instance of DrivePwdMap
|
/// Global singleton instance of DrivePwdMap
|
||||||
static DRIVE_PWD_MAP: Lazy<Mutex<DrivePWDmap>> = Lazy::new(|| Mutex::new(DrivePWDmap::new()));
|
static DRIVE_PWD_MAP: Lazy<Mutex<Drive2PWDmap >> = Lazy::new(|| Mutex::new(Drive2PWDmap::new()));
|
||||||
|
|
||||||
/// Public API to access the singleton instance
|
/// Public API to access the singleton instance
|
||||||
fn get_drive_pwd_map() -> &'static Mutex<DrivePWDmap> {
|
fn get_drive_pwd_map() -> &'static Mutex<Drive2PWDmap> {
|
||||||
&DRIVE_PWD_MAP
|
&DRIVE_PWD_MAP
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +141,6 @@ fn get_drive_pwd_map() -> &'static Mutex<DrivePWDmap> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_singleton_set_and_get_pwd() {
|
fn test_singleton_set_and_get_pwd() {
|
||||||
@ -165,7 +164,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_expand_path() {
|
fn test_expand_path() {
|
||||||
let mut drive_map = DrivePWDmap::new();
|
let mut drive_map = Drive2PWDmap::new();
|
||||||
|
|
||||||
// Set PWD for drive C
|
// Set PWD for drive C
|
||||||
drive_map.set_pwd(Path::new("C:\\Users\\Home")).unwrap();
|
drive_map.set_pwd(Path::new("C:\\Users\\Home")).unwrap();
|
||||||
@ -189,7 +188,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_and_get_pwd() {
|
fn test_set_and_get_pwd() {
|
||||||
let mut drive_map = DrivePWDmap::new();
|
let mut drive_map = Drive2PWDmap::new();
|
||||||
|
|
||||||
// Set PWD for drive C
|
// Set PWD for drive C
|
||||||
assert!(drive_map.set_pwd(Path::new("C:\\Users\\Example")).is_ok());
|
assert!(drive_map.set_pwd(Path::new("C:\\Users\\Example")).is_ok());
|
||||||
@ -205,7 +204,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_pwd_invalid_path() {
|
fn test_set_pwd_invalid_path() {
|
||||||
let mut drive_map = DrivePWDmap::new();
|
let mut drive_map = Drive2PWDmap::new();
|
||||||
|
|
||||||
// Invalid path (no drive letter)
|
// Invalid path (no drive letter)
|
||||||
let result = drive_map.set_pwd(Path::new("\\InvalidPath"));
|
let result = drive_map.set_pwd(Path::new("\\InvalidPath"));
|
||||||
@ -215,7 +214,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_pwd_invalid_drive() {
|
fn test_get_pwd_invalid_drive() {
|
||||||
let mut drive_map = DrivePWDmap::new();
|
let mut drive_map = Drive2PWDmap::new();
|
||||||
|
|
||||||
// Get PWD for a drive not set (e.g., Z)
|
// Get PWD for a drive not set (e.g., Z)
|
||||||
assert_eq!(drive_map.get_pwd('Z'), Some("Z:\\".to_string()));
|
assert_eq!(drive_map.get_pwd('Z'), Some("Z:\\".to_string()));
|
||||||
@ -223,27 +222,11 @@ mod tests {
|
|||||||
// Invalid drive letter (non-alphabetic)
|
// Invalid drive letter (non-alphabetic)
|
||||||
assert_eq!(drive_map.get_pwd('1'), None);
|
assert_eq!(drive_map.get_pwd('1'), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_drive_to_index() {
|
|
||||||
// Valid drive letters
|
|
||||||
assert_eq!(DrivePWDmap::drive_to_index('A'), Some(0));
|
|
||||||
assert_eq!(DrivePWDmap::drive_to_index('Z'), Some(25));
|
|
||||||
// Valid drive letters
|
|
||||||
assert_eq!(DrivePWDmap::drive_to_index('a'), Some(0));
|
|
||||||
assert_eq!(DrivePWDmap::drive_to_index('z'), Some(25));
|
|
||||||
for i in 1..25 {
|
|
||||||
assert_eq!(DrivePWDmap::drive_to_index(std::char::from_u32(('A' as usize + i) as u32).unwrap()), Some(i));
|
|
||||||
assert_eq!(DrivePWDmap::drive_to_index(std::char::from_u32(('a' as usize + i) as u32).unwrap()), Some(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid drive letters
|
}} // cfg_if! if #[cfg(windows)]
|
||||||
assert_eq!(DrivePWDmap::drive_to_index('1'), None);
|
|
||||||
assert_eq!(DrivePWDmap::drive_to_index('$'), None);
|
|
||||||
}
|
|
||||||
}}}
|
|
||||||
|
|
||||||
/// Usage for pwd_per_drive
|
/// Usage for pwd_per_drive on windows
|
||||||
///
|
///
|
||||||
/// Upon change PWD, call set_pwd_per_drive() with absolute path
|
/// Upon change PWD, call set_pwd_per_drive() with absolute path
|
||||||
///
|
///
|
||||||
@ -251,6 +234,9 @@ mod tests {
|
|||||||
///
|
///
|
||||||
/// Doctest
|
/// Doctest
|
||||||
/// ```Rust
|
/// ```Rust
|
||||||
|
/// use pwd_per_drive::pwd_per_drive_singleton::*;
|
||||||
|
///
|
||||||
|
/// if cfg!(windows) {
|
||||||
/// // Set PWD for drive C
|
/// // Set PWD for drive C
|
||||||
/// set_pwd_per_drive(Path::new("C:\\Users\\Home")).unwrap();
|
/// set_pwd_per_drive(Path::new("C:\\Users\\Home")).unwrap();
|
||||||
///
|
///
|
||||||
@ -269,82 +255,79 @@ mod tests {
|
|||||||
/// // Expand with no PWD set for the drive
|
/// // Expand with no PWD set for the drive
|
||||||
/// let expanded = expand_pwd_per_drive(Path::new("D:test"));
|
/// let expanded = expand_pwd_per_drive(Path::new("D:test"));
|
||||||
/// assert_eq!(expanded, Some(PathBuf::from("D:\\test")));
|
/// assert_eq!(expanded, Some(PathBuf::from("D:\\test")));
|
||||||
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub mod pwd_per_drive_singleton {
|
pub mod pwd_per_drive_singleton {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(windows)]
|
||||||
use super::get_drive_pwd_map;
|
use super::get_drive_pwd_map;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
/// set_pwd_per_drive
|
/// set_pwd_per_drive
|
||||||
/// record PWD for drive, path must be absolute path
|
/// record PWD for drive, path must be absolute path
|
||||||
/// return Ok(()) if succeeded, otherwise error message
|
/// return Ok(()) if succeeded, otherwise error message
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn set_pwd_per_drive(path: &Path) -> Result<(), String> {
|
pub fn set_pwd_per_drive(path: &Path) -> Result<(), String> {
|
||||||
|
cfg_if::cfg_if! { if #[cfg(target_os="windows")] {
|
||||||
|
|
||||||
if let Ok(mut pwd_per_drive) = get_drive_pwd_map().lock() {
|
if let Ok(mut pwd_per_drive) = get_drive_pwd_map().lock() {
|
||||||
pwd_per_drive.set_pwd(path)
|
pwd_per_drive.set_pwd(path)
|
||||||
} else {
|
} else {
|
||||||
Err("Failed to lock DrivePWDmap".to_string())
|
Err("Failed to lock map".to_string())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
} else {
|
||||||
pub fn set_pwd_per_drive(_path: &Path) -> Result<(), String> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// expand_pwe_per_drive
|
/// expand_pwe_per_drive
|
||||||
/// input relative path, expand PWD to construct absolute path
|
/// On windows, input relative path, expand PWD-per-drive to construct absolute path
|
||||||
/// return PathBuf for absolute path, None if input path is invalid
|
/// return PathBuf for absolute path, None if input path is invalid.
|
||||||
#[cfg(target_os = "windows")]
|
/// Otherwise, return None.
|
||||||
pub fn expand_pwd_per_drive(path: &Path) -> Option<PathBuf> {
|
pub fn expand_pwd_per_drive(path: &Path) -> Option<PathBuf> {
|
||||||
|
cfg_if::cfg_if! { if #[cfg(target_os="windows")] {
|
||||||
|
|
||||||
if need_expand_pwd_per_drive(path) {
|
if need_expand_pwd_per_drive(path) {
|
||||||
if let Ok(mut pwd_per_drive) = get_drive_pwd_map().lock() {
|
if let Ok(mut pwd_per_drive) = get_drive_pwd_map().lock() {
|
||||||
return pwd_per_drive.expand_path(path);
|
return pwd_per_drive.expand_path(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// expand_pwd_per_drive will return None on non-windows platform
|
/// On windows, if input path is relative path with drive letter,
|
||||||
#[cfg(not(target_os = "windows"))]
|
/// it can be expanded with PWD-per-drive.
|
||||||
pub fn expand_pwd_per_drive(_path: &Path) -> Option<PathBuf> {
|
/// On other platforms return false.
|
||||||
if need_expand_pwd_per_drive(_path) {
|
fn need_expand_pwd_per_drive(_path: &Path) -> bool {
|
||||||
// To DO
|
cfg_if::cfg_if! { if #[cfg(target_os="windows")] {
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If input path is relative path with drive letter,
|
if let Some(path_str) = _path.to_str() {
|
||||||
/// it can be expanded with PWD per drive
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
fn need_expand_pwd_per_drive(path: &Path) -> bool {
|
|
||||||
if let Some(path_str) = path.to_str() {
|
|
||||||
let chars: Vec<char> = path_str.chars().collect();
|
let chars: Vec<char> = path_str.chars().collect();
|
||||||
if chars.len() >= 2 {
|
if chars.len() >= 2 {
|
||||||
return chars[1] == ':'
|
return chars[1] == ':'
|
||||||
&& (chars.len() == 2 || (chars[2] != '/' && chars[2] != '\\'));
|
&& (chars.len() == 2 || (chars[2] != '/' && chars[2] != '\\'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// On non-windows platform, will not expand
|
}}
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
fn need_expand_pwd_per_drive(_path: &Path) -> bool {
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_usage_for_pwd_per_drive() {
|
fn test_usage_for_pwd_per_drive() {
|
||||||
|
if cfg!(windows) {
|
||||||
// Set PWD for drive C
|
// Set PWD for drive C
|
||||||
set_pwd_per_drive(Path::new("C:\\Users\\Home")).unwrap();
|
set_pwd_per_drive(Path::new("C:\\Users\\Home")).unwrap();
|
||||||
|
|
||||||
// Expand a relative path
|
// Expand a relative path
|
||||||
let expanded = expand_pwd_per_drive(Path::new("C:test"));
|
let expanded = expand_pwd_per_drive(Path::new("C:test"));
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
assert_eq!(expanded, Some(PathBuf::from("C:\\Users\\Home\\test")));
|
assert_eq!(expanded, Some(PathBuf::from("C:\\Users\\Home\\test")));
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
assert_eq!(expanded, None);
|
|
||||||
|
|
||||||
// Will NOT expand an absolute path
|
// Will NOT expand an absolute path
|
||||||
let expanded = expand_pwd_per_drive(Path::new("C:\\absolute\\path"));
|
let expanded = expand_pwd_per_drive(Path::new("C:\\absolute\\path"));
|
||||||
@ -356,9 +339,7 @@ pub mod pwd_per_drive_singleton {
|
|||||||
|
|
||||||
// Expand with no PWD set for the drive
|
// Expand with no PWD set for the drive
|
||||||
let expanded = expand_pwd_per_drive(Path::new("D:test"));
|
let expanded = expand_pwd_per_drive(Path::new("D:test"));
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
assert_eq!(expanded, Some(PathBuf::from("D:\\test")));
|
assert_eq!(expanded, Some(PathBuf::from("D:\\test")));
|
||||||
#[cfg(not(target_os = "windows"))]
|
}
|
||||||
assert_eq!(expanded, None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user