1#![cfg_attr(not(target_os = "windows"), allow(unused))]
2#[cfg(windows)]
3use crate::{Drives, PathExt};
4use std::{
5 borrow::Cow,
6 fs, io,
7 path::{Path, PathBuf},
8};
9
10#[derive(Default)]
11pub struct SimpleUnc {
12 pub map_to_drive: bool,
23
24 pub skip_dunce: bool,
28}
29
30impl SimpleUnc {
31 pub fn canonicalize(&self, path: impl AsRef<Path>) -> io::Result<PathBuf> {
32 let canonicalized = fs::canonicalize(path)?;
33 #[cfg(windows)]
34 if let Some(simplified) = self.simplify(&canonicalized)? {
35 return Ok(simplified.into_owned());
36 }
37 Ok(canonicalized)
38 }
39
40 pub fn simplify<'a>(&self, path: &'a Path) -> io::Result<Option<Cow<'a, Path>>> {
41 #[cfg(windows)]
42 return self._simplify(path).map_err(io_error_from_anyhow);
43 #[cfg(not(windows))]
44 Ok(None)
45 }
46
47 #[cfg(windows)]
48 fn _simplify<'a>(&self, path: &'a Path) -> anyhow::Result<Option<Cow<'a, Path>>> {
49 if let Some(drive_path) = Drives::drive_path(path)? {
51 if self.map_to_drive {
52 return Ok(Some(Cow::Owned(drive_path.to_path_buf())));
53 }
54 if let Some(unc) = path.unc_from_win32_file_namespace() {
55 return Ok(Some(Cow::Owned(unc)));
56 }
57 }
58
59 if !self.skip_dunce {
60 let simplified = dunce::simplified(path);
62 if !std::ptr::eq(path, simplified) {
63 return Ok(Some(Cow::Borrowed(simplified)));
64 }
65 }
66 Ok(None)
67 }
68
69 pub fn refresh() -> io::Result<()> {
71 #[cfg(windows)]
72 Drives::refresh().map_err(io_error_from_anyhow)?;
73 Ok(())
74 }
75}
76
77fn io_error_from_anyhow(error: anyhow::Error) -> io::Error {
78 match error.downcast::<io::Error>() {
79 Ok(io_error) => io_error,
80 Err(other_error) => io::Error::other(other_error),
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn simplify_none() {
90 let unc = SimpleUnc::default();
91 assert_eq!(unc.simplify(Path::new(r"C:\foo")).unwrap(), None);
92 }
93
94 #[cfg(windows)]
95 #[test]
96 fn simplify_dunce() {
97 let unc = SimpleUnc::default();
98 assert_eq!(
99 unc.simplify(Path::new(r"\\?\C:\foo")).unwrap(),
100 Some(Cow::Borrowed(Path::new(r"C:\foo")))
101 );
102 }
103
104 #[cfg(windows)]
105 #[test]
106 fn simplify_dunce_skip() {
107 let unc = SimpleUnc {
108 skip_dunce: true,
109 ..Default::default()
110 };
111 assert_eq!(unc.simplify(Path::new(r"\\?\C:\foo")).unwrap(), None);
112 }
113}