I tried to develop a Rust lib to assist with swapping filenames for any two files, but I had problems when I tried to dealing with paths on WSL.
| File1 | File2 | |
|---|---|---|
| Before | D:\test\1.ext1 |
D:\test\2.ext2 |
| After | D:\test\2.ext1 |
D:\test\1.ext2 |
| File1 | File2 | |
|---|---|---|
| Before | ./1.ext1 |
~/2.ext2 |
| After | D:\test\2.ext1 |
C:\Users\xxx\1.ext2 |
| File1 | File2 | |
|---|---|---|
| Before | \\wsl.localhost\Debian\home\user1\1.ext1 |
\\wsl.localhost\Debian\home\user1\2.ext2 |
| After (WHAT I WANT) | \\wsl.localhost\Debian\home\user1\2.ext1 |
\\wsl.localhost\Debian\home\user1\1.ext2 |
MWE:
use std::path::{Path, PathBuf};
fn main() {
// Please create this file in your wsl for test.
// `~/1.ext1`
// MODIFY THIS with your wsl user name
let wsl_user_name = "";
// Please create this on working dir (windows) for test.
// `./2.ext2`
let current_exe = std::env::current_exe().unwrap();
let base_dir = current_exe.parent().unwrap();
let wsl_file = PathBuf::from(format!(
r"\\wsl.localhost\Debian\home\{}\1.ext1",
wsl_user_name
));
let win_file = PathBuf::from(r"./2.ext2");
let result_wsl = path_get_absolute_exist(&wsl_file, base_dir);
dbg!(result_wsl);
let result_win = path_get_absolute_exist(&win_file, base_dir);
dbg!(result_win);
}
fn path_get_absolute_exist(path: &Path, base_dir: &Path) -> (bool, PathBuf) {
if path.as_os_str().is_empty() {
return (false, path.to_path_buf());
}
let mut path = path.to_path_buf();
#[cfg(windows)]
{
use std::path::{Component, Prefix};
path = {
let temp = path.to_str().unwrap_or("").replace("/", "\\");
PathBuf::from(temp)
};
let is_absolute = {
let mut components = path.components();
if let Some(Component::Prefix(prefix_component)) = components.next() {
let has_root_dir = matches!(components.next(), Some(Component::RootDir));
dbg!(has_root_dir);
if !has_root_dir {
false
} else {
dbg!(prefix_component.kind());
matches!(
prefix_component.kind(), //Useful for judge `\\wsl.localhost\` but useless when test exist()
Prefix::VerbatimUNC(..)
| Prefix::UNC(..)
| Prefix::VerbatimDisk(..)
| Prefix::Disk(_)
| Prefix::DeviceNS(..)
| Prefix::Verbatim(_)
)
}
} else {
dbg!(path.is_absolute());
path.is_absolute()
}
};
if !is_absolute {
if path.starts_with("~") {
if let Ok(home_dir) = std::env::var("USERPROFILE") {
let mut new_path = PathBuf::from(home_dir);
let remaining = path.strip_prefix("~/").ok();
if let Some(rem) = remaining {
new_path.push(rem);
path = new_path;
} else if path.to_string_lossy() == "~" {
path = new_path;
} else {
// "~something"
path = base_dir.join(path);
}
}
} else {
path = base_dir.join(path);
}
}
dbg!(format!("Path Final: {}", &path.display()));
}
#[cfg(not(windows))]
{
path = {
let temp = path.to_str().unwrap_or("").replace("\\", "/");
PathBuf::from(temp)
};
if !path.is_absolute() {
if path.starts_with("~") {
if let Ok(home_dir) = std::env::var("HOME") {
let mut new_path = PathBuf::from(home_dir);
if let Some(remaining) = path.strip_prefix("~/") {
new_path.push(remaining);
} else if path.to_string_lossy() == "~" {
// Just "~", so it's the home directory
}
path = new_path;
}
} else {
path = base_dir.join(path);
}
}
dbg!(format!("Path Final: {}", &path.display()));
}
let canonical = path.canonicalize();
match canonical {
Ok(x) => (x.exists(), x),
Err(e) => {
eprintln!("{}", e);
(path.exists(), path)
}
}
}
[src\exchange.rs:161:13] is_absolute = true
[src\exchange.rs:190:9] &path = "\\\\wsl.localhost\\Debian\\home\\user1\\1.ext1"
[src\exchange.rs:161:13] is_absolute = true
[src\exchange.rs:190:9] &path = "\\\\wsl.localhost\\Debian\\home\\user1\\2.ext2"
File does not exist
I didn't give all the code here for length reasons, if you need all the code, please check it out at https://github.com/Mikachu2333/exchange_name_lib
For recognize WSL path, one should use std::path::{Component, Prefix} in order to recognize WSL path such as \\\\wsl.localhost\\Debian\\home\\LinkChou\\1.ext1
Although WSL path is "absolute" in the view of users, rust would not recognize it as 'absolute', but PathBuf.join(new) would replace the old as the following example shows.
let dir = =std::env::current_dir().unwrap();
let file = r"\\\\wsl.localhost\\Debian\\home\\LinkChou\\1.ext1";
let path = PathBuf::from(file);
assert(path.is_absolute());//panic
let result = dir.join(path);
assert(result.to_path_buf(),dir.to_path_buf())//true
Complete code as the following shows.
use std::path::{Path, PathBuf};
fn main() {
// Please create this file in your wsl for test before run.
// `~/1.ext1`
// MODIFY THIS with your wsl user name
let wsl_user_name = "LinkChou";
let wsl_type = "Debian";
// Please create this on working dir (windows) for test.
// `./2.ext2`
let current_exe = std::env::current_exe().unwrap();
let base_dir = current_exe.parent().unwrap();
// test files path
let wsl_file = PathBuf::from(format!(
r"\\wsl.localhost\{}\home\{}\1.ext1",
wsl_type, wsl_user_name
));
let win_file = PathBuf::from(r"2.ext2");
let result_wsl = path_get_absolute_exist(&wsl_file, base_dir);
dbg!(&result_wsl);//true, wsl file path(same as original str)
assert!(result_wsl.1.exists());//true
let result_win = path_get_absolute_exist(&win_file, base_dir);
dbg!(result_win);// false, return absolute path
}
fn path_get_absolute_exist(path: &Path, base_dir: &Path) -> (bool, PathBuf) {
if path.as_os_str().is_empty() {
return (false, path.to_path_buf());
}
let mut path = path.to_path_buf();
//only judge wsl,... .etc on win
#[cfg(windows)]
{
use std::path::{Component, Prefix};
path = {
let temp = path.to_str().unwrap_or("").replace("/", "\\");
PathBuf::from(temp)
};
let is_absolute = {
let mut components = path.components();
if let Some(Component::Prefix(prefix_component)) = components.next() {
let has_root_dir = matches!(components.next(), Some(Component::RootDir));
dbg!(has_root_dir);
if !has_root_dir {
false
} else {
dbg!(prefix_component.kind());
matches!(
prefix_component.kind(), //Useful for judge `\\wsl.localhost\` but useless when test exist()
Prefix::VerbatimUNC(..)
| Prefix::UNC(..)
| Prefix::VerbatimDisk(..)
| Prefix::Disk(_)
| Prefix::DeviceNS(..)
| Prefix::Verbatim(_)
)
}
} else {
dbg!(path.is_absolute());
path.is_absolute()
}
};
if !is_absolute {
if path.starts_with("~") {
if let Ok(home_dir) = std::env::var("USERPROFILE") {
let mut new_path = PathBuf::from(home_dir);
let remaining = path.strip_prefix("~/").ok();
if let Some(rem) = remaining {
new_path.push(rem);
path = new_path;
} else if path.to_string_lossy() == "~" {
path = new_path;
} else {
// "~something"
path = base_dir.join(path);
}
}
} else if path.starts_with(".") {
let remaining = path.strip_prefix(".\\").ok();
path = base_dir.join(remaining.unwrap());
} else {
path = base_dir.join(path);
}
}
dbg!(format!("Path Final: {}", &path.display()));
}
#[cfg(not(windows))]
{
path = {
let temp = path.to_str().unwrap_or("").replace("\\", "/");
PathBuf::from(temp)
};
if !path.is_absolute() {
if path.starts_with("~") {
if let Ok(home_dir) = std::env::var("HOME") {
let mut new_path = PathBuf::from(home_dir);
if let Some(remaining) = path.strip_prefix("~/") {
new_path.push(remaining);
} else if path.to_string_lossy() == "~" {
// Just "~", so it's the home directory
}
path = new_path;
}
} else if path.starts_with(".") {
let remaining = path.strip_prefix("./").ok();
path = base_dir.join(remaining.unwrap());
} else {
path = base_dir.join(path);
}
}
dbg!(format!("Path Final: {}", &path.display()));
}
let canonical = path.canonicalize();//we have to use canonicalize here because it will panic if path's type is wsl
match canonical {
Ok(x) => (x.exists(), x),
Err(e) => {
eprintln!("{}", e);
(path.exists(), path)
}
}
}
path.is_absolute() and path.canonicalize() for avoiding panic and resolve wsl like odd path.