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
63fn expand_path(path: impl AsRef<Path>, need_expand_tilde: bool) -> PathBuf {
64 let path = if need_expand_tilde {
65 expand_tilde(path)
66 } else {
67 PathBuf::from(path.as_ref())
68 };
69 let path = expand_ndots(path);
70 expand_dots(path)
71}
72
73pub fn expand_path_with<P, Q>(path: P, relative_to: Q, expand_tilde: bool) -> PathBuf
83where
84 P: AsRef<Path>,
85 Q: AsRef<Path>,
86{
87 let path = join_path_relative(path, relative_to, expand_tilde);
88
89 expand_path(path, expand_tilde)
90}
91
92pub fn expand_to_real_path<P>(path: P) -> PathBuf
99where
100 P: AsRef<Path>,
101{
102 let path = expand_tilde(path);
103 expand_ndots(path)
104}
105
106pub fn locate_in_dirs<I, P>(
110 filename: impl AsRef<Path>,
111 cwd: impl AsRef<Path>,
112 dirs: impl FnOnce() -> I,
113) -> std::io::Result<PathBuf>
114where
115 I: IntoIterator<Item = P>,
116 P: AsRef<Path>,
117{
118 let filename = filename.as_ref();
119 let cwd = cwd.as_ref();
120 match canonicalize_with(filename, cwd) {
121 Ok(path) => Ok(path),
122 Err(err) => {
123 let mut found = None;
125 for dir in dirs() {
126 if let Ok(path) =
127 canonicalize_with(dir, cwd).and_then(|dir| canonicalize_with(filename, dir))
128 {
129 found = Some(path);
130 break;
131 }
132 }
133 found.ok_or(err)
134 }
135 }
136}