aboutsummaryrefslogtreecommitdiff
path: root/crates/paths
diff options
context:
space:
mode:
Diffstat (limited to 'crates/paths')
-rw-r--r--crates/paths/Cargo.toml9
-rw-r--r--crates/paths/src/lib.rs217
2 files changed, 226 insertions, 0 deletions
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml
new file mode 100644
index 000000000..cbe2c26e2
--- /dev/null
+++ b/crates/paths/Cargo.toml
@@ -0,0 +1,9 @@
1[package]
2name = "paths"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6license = "MIT OR Apache-2.0"
7
8[lib]
9doctest = false
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
new file mode 100644
index 000000000..1b259682d
--- /dev/null
+++ b/crates/paths/src/lib.rs
@@ -0,0 +1,217 @@
1//! Thin wrappers around `std::path`, distinguishing between absolute and
2//! relative paths.
3use std::{
4 convert::{TryFrom, TryInto},
5 ops,
6 path::{Component, Path, PathBuf},
7};
8
9#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
10pub struct AbsPathBuf(PathBuf);
11
12impl From<AbsPathBuf> for PathBuf {
13 fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf {
14 path_buf
15 }
16}
17
18impl ops::Deref for AbsPathBuf {
19 type Target = AbsPath;
20 fn deref(&self) -> &AbsPath {
21 self.as_path()
22 }
23}
24
25impl AsRef<Path> for AbsPathBuf {
26 fn as_ref(&self) -> &Path {
27 self.0.as_path()
28 }
29}
30
31impl AsRef<AbsPath> for AbsPathBuf {
32 fn as_ref(&self) -> &AbsPath {
33 self.as_path()
34 }
35}
36
37impl TryFrom<PathBuf> for AbsPathBuf {
38 type Error = PathBuf;
39 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
40 if !path_buf.is_absolute() {
41 return Err(path_buf);
42 }
43 Ok(AbsPathBuf(path_buf))
44 }
45}
46
47impl TryFrom<&str> for AbsPathBuf {
48 type Error = PathBuf;
49 fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> {
50 AbsPathBuf::try_from(PathBuf::from(path))
51 }
52}
53
54impl PartialEq<AbsPath> for AbsPathBuf {
55 fn eq(&self, other: &AbsPath) -> bool {
56 self.as_path() == other
57 }
58}
59
60impl AbsPathBuf {
61 pub fn assert(path: PathBuf) -> AbsPathBuf {
62 AbsPathBuf::try_from(path)
63 .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display()))
64 }
65 pub fn as_path(&self) -> &AbsPath {
66 AbsPath::assert(self.0.as_path())
67 }
68 pub fn pop(&mut self) -> bool {
69 self.0.pop()
70 }
71}
72
73#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
74#[repr(transparent)]
75pub struct AbsPath(Path);
76
77impl ops::Deref for AbsPath {
78 type Target = Path;
79 fn deref(&self) -> &Path {
80 &self.0
81 }
82}
83
84impl AsRef<Path> for AbsPath {
85 fn as_ref(&self) -> &Path {
86 &self.0
87 }
88}
89
90impl<'a> TryFrom<&'a Path> for &'a AbsPath {
91 type Error = &'a Path;
92 fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
93 if !path.is_absolute() {
94 return Err(path);
95 }
96 Ok(AbsPath::assert(path))
97 }
98}
99
100impl AbsPath {
101 pub fn assert(path: &Path) -> &AbsPath {
102 assert!(path.is_absolute());
103 unsafe { &*(path as *const Path as *const AbsPath) }
104 }
105
106 pub fn parent(&self) -> Option<&AbsPath> {
107 self.0.parent().map(AbsPath::assert)
108 }
109 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
110 self.as_ref().join(path).try_into().unwrap()
111 }
112 pub fn normalize(&self) -> AbsPathBuf {
113 AbsPathBuf(normalize_path(&self.0))
114 }
115 pub fn to_path_buf(&self) -> AbsPathBuf {
116 AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
117 }
118 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
119 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
120 }
121}
122
123#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
124pub struct RelPathBuf(PathBuf);
125
126impl From<RelPathBuf> for PathBuf {
127 fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf {
128 path_buf
129 }
130}
131
132impl ops::Deref for RelPathBuf {
133 type Target = RelPath;
134 fn deref(&self) -> &RelPath {
135 self.as_path()
136 }
137}
138
139impl AsRef<Path> for RelPathBuf {
140 fn as_ref(&self) -> &Path {
141 self.0.as_path()
142 }
143}
144
145impl TryFrom<PathBuf> for RelPathBuf {
146 type Error = PathBuf;
147 fn try_from(path_buf: PathBuf) -> Result<RelPathBuf, PathBuf> {
148 if !path_buf.is_relative() {
149 return Err(path_buf);
150 }
151 Ok(RelPathBuf(path_buf))
152 }
153}
154
155impl TryFrom<&str> for RelPathBuf {
156 type Error = PathBuf;
157 fn try_from(path: &str) -> Result<RelPathBuf, PathBuf> {
158 RelPathBuf::try_from(PathBuf::from(path))
159 }
160}
161
162impl RelPathBuf {
163 pub fn as_path(&self) -> &RelPath {
164 RelPath::new_unchecked(self.0.as_path())
165 }
166}
167
168#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
169#[repr(transparent)]
170pub struct RelPath(Path);
171
172impl ops::Deref for RelPath {
173 type Target = Path;
174 fn deref(&self) -> &Path {
175 &self.0
176 }
177}
178
179impl AsRef<Path> for RelPath {
180 fn as_ref(&self) -> &Path {
181 &self.0
182 }
183}
184
185impl RelPath {
186 pub fn new_unchecked(path: &Path) -> &RelPath {
187 unsafe { &*(path as *const Path as *const RelPath) }
188 }
189}
190
191// https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
192fn normalize_path(path: &Path) -> PathBuf {
193 let mut components = path.components().peekable();
194 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
195 components.next();
196 PathBuf::from(c.as_os_str())
197 } else {
198 PathBuf::new()
199 };
200
201 for component in components {
202 match component {
203 Component::Prefix(..) => unreachable!(),
204 Component::RootDir => {
205 ret.push(component.as_os_str());
206 }
207 Component::CurDir => {}
208 Component::ParentDir => {
209 ret.pop();
210 }
211 Component::Normal(c) => {
212 ret.push(c);
213 }
214 }
215 }
216 ret
217}