1#[cfg(windows)]
2use omnipath::WinPathExt;
3use std::io;
4use std::path::{Path, PathBuf};
5
6use super::dots::{expand_dots, expand_ndots};
7use super::tilde::expand_tilde;
8
9fn join_path_relative<P, Q>(path: P, relative_to: Q, expand_tilde: bool) -> PathBuf
11where
12 P: AsRef<Path>,
13 Q: AsRef<Path>,
14{
15 let path = path.as_ref();
16 let relative_to = relative_to.as_ref();
17
18 if path == Path::new(".") {
19 relative_to.into()
23 } else if path.to_string_lossy().as_ref().starts_with('~') && expand_tilde {
24 path.into()
26 } else {
27 relative_to.join(path)
28 }
29}
30
31fn canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> {
32 let path = expand_tilde(path);
33 let path = expand_ndots(path);
34 canonicalize_path(&path)
35}
36
37#[cfg(windows)]
38fn canonicalize_path(path: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
39 path.canonicalize()?.to_winuser_path()
40}
41
42#[cfg(not(windows))]
43fn canonicalize_path(path: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
44 path.canonicalize()
45}
46
47pub fn canonicalize_with<P, Q>(path: P, relative_to: Q) -> io::Result<PathBuf>
54where
55 P: AsRef<Path>,
56 Q: AsRef<Path>,
57{
58 let path = join_path_relative(path, relative_to, true);
59
60 canonicalize(path)
61}
62
63pub fn expand_path(path: impl AsRef<Path>, need_expand_tilde: bool) -> PathBuf {
67 let path = if need_expand_tilde {
68 expand_tilde(path)
69 } else {
70 PathBuf::from(path.as_ref())
71 };
72 let path = expand_ndots(path);
73 expand_dots(path)
74}
75
76pub fn expand_path_with<P, Q>(path: P, relative_to: Q, expand_tilde: bool) -> PathBuf
86where
87 P: AsRef<Path>,
88 Q: AsRef<Path>,
89{
90 let path = join_path_relative(path, relative_to, expand_tilde);
91
92 expand_path(path, expand_tilde)
93}
94
95pub fn expand_to_real_path<P>(path: P) -> PathBuf
102where
103 P: AsRef<Path>,
104{
105 let path = expand_tilde(path);
106 expand_ndots(path)
107}
108
109pub fn locate_in_dirs<I, P>(
113 filename: impl AsRef<Path>,
114 cwd: impl AsRef<Path>,
115 dirs: impl FnOnce() -> I,
116) -> std::io::Result<PathBuf>
117where
118 I: IntoIterator<Item = P>,
119 P: AsRef<Path>,
120{
121 let filename = filename.as_ref();
122 let cwd = cwd.as_ref();
123 match canonicalize_with(filename, cwd) {
124 Ok(path) => Ok(path),
125 Err(err) => {
126 let mut found = None;
128 for dir in dirs() {
129 if let Ok(path) =
130 canonicalize_with(dir, cwd).and_then(|dir| canonicalize_with(filename, dir))
131 {
132 found = Some(path);
133 break;
134 }
135 }
136 found.ok_or(err)
137 }
138 }
139}