#![cfg_attr(not(target_os = "windows"), allow(unused))]
use crate::Display;
#[cfg(windows)]
use crate::{Drives, PathExt};
use std::{
borrow::Cow,
fs, io,
path::{Path, PathBuf},
};
#[derive(Debug, Default)]
pub struct SimpleUnc {
pub map_to_drive: bool,
pub skip_dunce: bool,
pub _unused: bool,
#[cfg(all(test, windows))]
drives: Option<Drives>,
}
impl SimpleUnc {
pub fn canonicalize(&self, path: impl AsRef<Path>) -> io::Result<PathBuf> {
let canonicalized = fs::canonicalize(path)?;
#[cfg(windows)]
if let Some(simplified) = self.simplify(&canonicalized)? {
return Ok(simplified.into_owned());
}
Ok(canonicalized)
}
pub fn simplify<'a>(&self, path: &'a Path) -> io::Result<Option<Cow<'a, Path>>> {
#[cfg(windows)]
return self._simplify(path).map_err(io_error_from_anyhow);
#[cfg(not(windows))]
Ok(None)
}
#[cfg(windows)]
fn _simplify<'a>(&self, path: &'a Path) -> anyhow::Result<Option<Cow<'a, Path>>> {
if let Some(drive_path) = self.drive_path(path)? {
if self.map_to_drive {
return Ok(Some(Cow::Owned(drive_path.to_path_buf())));
}
if let Some(unc) = path.unc_from_win32_file_namespace() {
return Ok(Some(Cow::Owned(unc)));
}
}
if !self.skip_dunce {
let simplified = dunce::simplified(path);
if !std::ptr::eq(path, simplified) {
return Ok(Some(Cow::Borrowed(simplified)));
}
}
Ok(None)
}
#[cfg(windows)]
fn drive_path<'a>(&self, path: &'a Path) -> anyhow::Result<Option<crate::DrivePath<'a>>> {
#[cfg(test)]
if let Some(drives) = &self.drives {
return Ok(drives._drive_path(path));
}
Drives::drive_path(path)
}
pub fn refresh() -> io::Result<()> {
#[cfg(windows)]
Drives::refresh().map_err(io_error_from_anyhow)?;
Ok(())
}
pub fn display<'a>(&'a self, path: &'a Path) -> Display<'a> {
Display::new(self, path)
}
#[cfg(all(test, windows))]
pub(crate) fn mock_with_drive() -> SimpleUnc {
SimpleUnc {
drives: Some(Drives::with_drives(vec![
('X', PathBuf::from(r"\\?\UNC\server\share")),
('Z', PathBuf::from(r"\\?\UNC\server2\share2")),
])),
..Default::default()
}
}
}
fn io_error_from_anyhow(error: anyhow::Error) -> io::Error {
match error.downcast::<io::Error>() {
Ok(io_error) => io_error,
Err(other_error) => io::Error::other(other_error),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simplify_not_simplified() {
let unc = SimpleUnc::default();
assert_eq!(unc.simplify(Path::new(r"C:\foo")).unwrap(), None);
}
#[cfg(windows)]
#[test]
fn simplify_drive_not_simplified() {
let unc = SimpleUnc::mock_with_drive();
assert_eq!(unc.simplify(Path::new(r"C:\foo")).unwrap(), None);
}
#[cfg(windows)]
#[test]
fn simplify_drive_unc() {
let mut unc = SimpleUnc::mock_with_drive();
let path = Path::new(r"\\?\UNC\server\share\foo");
let path2 = Path::new(r"\\?\UNC\server2\share2\foo2");
assert_eq!(
unc.simplify(path).unwrap(),
Some(Cow::Owned(PathBuf::from(r"\\server\share\foo")))
);
assert_eq!(
unc.simplify(path2).unwrap(),
Some(Cow::Owned(PathBuf::from(r"\\server2\share2\foo2")))
);
unc.map_to_drive = true;
assert_eq!(
unc.simplify(path).unwrap(),
Some(Cow::Owned(PathBuf::from(r"X:\foo")))
);
assert_eq!(
unc.simplify(path2).unwrap(),
Some(Cow::Owned(PathBuf::from(r"Z:\foo2")))
);
}
#[cfg(windows)]
#[test]
fn simplify_dunce() {
let unc = SimpleUnc::default();
assert_eq!(
unc.simplify(Path::new(r"\\?\C:\foo")).unwrap(),
Some(Cow::Borrowed(Path::new(r"C:\foo")))
);
}
#[cfg(windows)]
#[test]
fn simplify_dunce_skip() {
let unc = SimpleUnc {
skip_dunce: true,
..Default::default()
};
assert_eq!(unc.simplify(Path::new(r"\\?\C:\foo")).unwrap(), None);
}
}