use std::{
ffi::{OsStr, OsString},
fs::canonicalize,
os::windows::ffi::{OsStrExt, OsStringExt},
path::{Path, PathBuf},
};
use crate::{ResolveOptions, Resolver};
use thiserror::Error;
pub fn volume_name_from_mount_point<S: AsRef<OsStr>>(
mount_point: S,
) -> Result<OsString, Win32Error> {
use windows_sys::Win32::{
Foundation::GetLastError, Storage::FileSystem::GetVolumeNameForVolumeMountPointW,
};
const BUFFER_SIZE: u32 = 64;
let mount_point: Vec<u16> = mount_point.as_ref().encode_wide().chain(Some(0)).collect();
let mut buffer = vec![0; BUFFER_SIZE as usize];
unsafe {
if GetVolumeNameForVolumeMountPointW(mount_point.as_ptr(), buffer.as_mut_ptr(), BUFFER_SIZE)
== 0
{
Err(Win32Error { error_code: GetLastError() })
} else {
let length = buffer.iter().position(|&c| c == 0).unwrap();
Ok(OsString::from_wide(&buffer[..length]))
}
}
}
pub fn get_dos_device_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, Win32Error> {
let path = path.as_ref();
assert!(path.has_root(), "Expected a path with a root");
let root = {
let mut root = OsString::from(path.components().next().unwrap().as_os_str());
root.push(r"\");
root
};
let mut volume_name_root: Vec<u16> =
volume_name_from_mount_point(root)?.encode_wide().collect();
if volume_name_root.starts_with(&[92, 92, 63, 92] ) {
volume_name_root[2] = u16::from(b'.');
}
let mut dos_device_path = PathBuf::from(OsString::from_wide(&volume_name_root));
dos_device_path.extend(path.components().skip(1));
Ok(dos_device_path)
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("Win32 Error (GetLastError: {error_code:#X})")]
pub struct Win32Error {
pub error_code: u32,
}
#[test]
fn test_get_dos_device_path() {
let root = super::fixture_root();
println!("Fixture root: {root:?}");
let dos_device_path = get_dos_device_path(&root).unwrap();
println!("Dos device path: {dos_device_path:?}");
let canonical_dos_device_path = canonicalize(&dos_device_path).unwrap();
println!("-> Canonicalized: {canonical_dos_device_path:?}");
assert_eq!(canonical_dos_device_path, canonicalize(root).unwrap());
}
#[test]
fn forward_slash_path_resolved_to_backslash() {
let expected = super::fixture_root().join("enhanced-resolve").join("lib").join("index.js");
let specifier = expected.to_string_lossy().replace('\\', "/");
let resolver_with_symlinks = Resolver::default(); let resolver_without_symlinks =
Resolver::new(ResolveOptions { symlinks: false, ..ResolveOptions::default() });
let dir = super::fixture_root();
let expected = expected.to_string_lossy().to_string();
let resolved = resolver_with_symlinks
.resolve(&dir, &specifier)
.map(|r| r.into_path_buf().to_string_lossy().to_string());
assert_eq!(resolved, Ok(expected.clone()), "symlinks: true");
let resolved = resolver_without_symlinks
.resolve(&dir, &specifier)
.map(|r| r.into_path_buf().to_string_lossy().to_string());
assert_eq!(resolved, Ok(expected), "symlinks: false");
}