1#[cfg(windows)]
2use std::path::{Component, Prefix};
3use std::path::{Path, PathBuf};
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
11pub fn data_dir() -> Option<AbsolutePathBuf> {
13 configurable_dir_path("XDG_DATA_HOME", dirs::data_dir)
14}
15
16pub fn cache_dir() -> Option<AbsolutePathBuf> {
18 configurable_dir_path("XDG_CACHE_HOME", dirs::cache_dir)
19}
20
21pub fn nu_config_dir() -> Option<AbsolutePathBuf> {
23 configurable_dir_path("XDG_CONFIG_HOME", dirs::config_dir).map(|mut p| {
24 p.push("nushell");
25 p
26 })
27}
28
29fn configurable_dir_path(
30 name: &'static str,
31 dir: impl FnOnce() -> Option<PathBuf>,
32) -> Option<AbsolutePathBuf> {
33 std::env::var(name)
34 .ok()
35 .and_then(|path| AbsolutePathBuf::try_from(path).ok())
36 .or_else(|| dir().and_then(|path| AbsolutePathBuf::try_from(path).ok()))
37 .map(|path| path.canonicalize().map(Into::into).unwrap_or(path))
38}
39
40#[cfg(windows)]
49pub fn is_windows_device_path(path: &Path) -> bool {
50 match path.components().next() {
51 Some(Component::Prefix(prefix)) if matches!(prefix.kind(), Prefix::DeviceNS(_)) => {
52 return true;
53 }
54 _ => {}
55 }
56 let special_paths: [&Path; 28] = [
57 Path::new("CON"),
58 Path::new("PRN"),
59 Path::new("AUX"),
60 Path::new("NUL"),
61 Path::new("COM1"),
62 Path::new("COM2"),
63 Path::new("COM3"),
64 Path::new("COM4"),
65 Path::new("COM5"),
66 Path::new("COM6"),
67 Path::new("COM7"),
68 Path::new("COM8"),
69 Path::new("COM9"),
70 Path::new("COM¹"),
71 Path::new("COM²"),
72 Path::new("COM³"),
73 Path::new("LPT1"),
74 Path::new("LPT2"),
75 Path::new("LPT3"),
76 Path::new("LPT4"),
77 Path::new("LPT5"),
78 Path::new("LPT6"),
79 Path::new("LPT7"),
80 Path::new("LPT8"),
81 Path::new("LPT9"),
82 Path::new("LPT¹"),
83 Path::new("LPT²"),
84 Path::new("LPT³"),
85 ];
86 if special_paths.contains(&path) {
87 return true;
88 }
89 false
90}
91
92#[cfg(not(windows))]
93pub fn is_windows_device_path(_path: &Path) -> bool {
94 false
95}
96
97#[cfg(test)]
98mod test_is_windows_device_path {
99 use crate::is_windows_device_path;
100 use std::path::Path;
101
102 #[cfg_attr(not(windows), ignore = "only for Windows")]
103 #[test]
104 fn device_namespace() {
105 assert!(is_windows_device_path(Path::new(r"\\.\CON")))
106 }
107
108 #[cfg_attr(not(windows), ignore = "only for Windows")]
109 #[test]
110 fn reserved_device_name() {
111 assert!(is_windows_device_path(Path::new(r"NUL")))
112 }
113
114 #[cfg_attr(not(windows), ignore = "only for Windows")]
115 #[test]
116 fn normal_path() {
117 assert!(!is_windows_device_path(Path::new(r"dir\file")))
118 }
119
120 #[cfg_attr(not(windows), ignore = "only for Windows")]
121 #[test]
122 fn absolute_path() {
123 assert!(!is_windows_device_path(Path::new(r"\dir\file")))
124 }
125
126 #[cfg_attr(not(windows), ignore = "only for Windows")]
127 #[test]
128 fn unc_path() {
129 assert!(!is_windows_device_path(Path::new(r"\\server\share")))
130 }
131
132 #[cfg_attr(not(windows), ignore = "only for Windows")]
133 #[test]
134 fn verbatim_path() {
135 assert!(!is_windows_device_path(Path::new(r"\\?\dir\file")))
136 }
137}