Skip to main content

nu_path/
helpers.rs

1use std::path::Path;
2#[cfg(windows)]
3use std::path::{Component, Prefix};
4
5use crate::AbsolutePathBuf;
6
7pub fn home_dir() -> Option<AbsolutePathBuf> {
8    dirs::home_dir().and_then(|home| AbsolutePathBuf::try_from(home).ok())
9}
10
11// List of special paths that can be written to and/or read from, even though they
12// don't appear as directory entries.
13// See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
14// In rare circumstances, reserved paths _can_ exist as regular files in a
15// directory which shadow their special counterpart, so the safe way of referring
16// to these paths is by prefixing them with '\\.\' (this instructs the Windows APIs
17// to access the Win32 device namespace instead of the Win32 file namespace)
18// https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
19#[cfg(windows)]
20pub fn is_windows_device_path(path: &Path) -> bool {
21    match path.components().next() {
22        Some(Component::Prefix(prefix)) if matches!(prefix.kind(), Prefix::DeviceNS(_)) => {
23            return true;
24        }
25        _ => {}
26    }
27    let special_paths: [&Path; 28] = [
28        Path::new("CON"),
29        Path::new("PRN"),
30        Path::new("AUX"),
31        Path::new("NUL"),
32        Path::new("COM1"),
33        Path::new("COM2"),
34        Path::new("COM3"),
35        Path::new("COM4"),
36        Path::new("COM5"),
37        Path::new("COM6"),
38        Path::new("COM7"),
39        Path::new("COM8"),
40        Path::new("COM9"),
41        Path::new("COM¹"),
42        Path::new("COM²"),
43        Path::new("COM³"),
44        Path::new("LPT1"),
45        Path::new("LPT2"),
46        Path::new("LPT3"),
47        Path::new("LPT4"),
48        Path::new("LPT5"),
49        Path::new("LPT6"),
50        Path::new("LPT7"),
51        Path::new("LPT8"),
52        Path::new("LPT9"),
53        Path::new("LPT¹"),
54        Path::new("LPT²"),
55        Path::new("LPT³"),
56    ];
57    if special_paths.contains(&path) {
58        return true;
59    }
60    false
61}
62
63#[cfg(not(windows))]
64pub fn is_windows_device_path(_path: &Path) -> bool {
65    false
66}
67
68#[cfg(test)]
69mod test_is_windows_device_path {
70    use crate::is_windows_device_path;
71    use std::path::Path;
72
73    #[cfg_attr(not(windows), ignore = "only for Windows")]
74    #[test]
75    fn device_namespace() {
76        assert!(is_windows_device_path(Path::new(r"\\.\CON")))
77    }
78
79    #[cfg_attr(not(windows), ignore = "only for Windows")]
80    #[test]
81    fn reserved_device_name() {
82        assert!(is_windows_device_path(Path::new("NUL")))
83    }
84
85    #[cfg_attr(not(windows), ignore = "only for Windows")]
86    #[test]
87    fn normal_path() {
88        assert!(!is_windows_device_path(Path::new(r"dir\file")))
89    }
90
91    #[cfg_attr(not(windows), ignore = "only for Windows")]
92    #[test]
93    fn absolute_path() {
94        assert!(!is_windows_device_path(Path::new(r"\dir\file")))
95    }
96
97    #[cfg_attr(not(windows), ignore = "only for Windows")]
98    #[test]
99    fn unc_path() {
100        assert!(!is_windows_device_path(Path::new(r"\\server\share")))
101    }
102
103    #[cfg_attr(not(windows), ignore = "only for Windows")]
104    #[test]
105    fn verbatim_path() {
106        assert!(!is_windows_device_path(Path::new(r"\\?\dir\file")))
107    }
108}