diff options
Diffstat (limited to 'crates/paths')
-rw-r--r-- | crates/paths/Cargo.toml | 9 | ||||
-rw-r--r-- | crates/paths/src/lib.rs | 217 |
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] | ||
2 | name = "paths" | ||
3 | version = "0.1.0" | ||
4 | authors = ["rust-analyzer developers"] | ||
5 | edition = "2018" | ||
6 | license = "MIT OR Apache-2.0" | ||
7 | |||
8 | [lib] | ||
9 | doctest = 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. | ||
3 | use std::{ | ||
4 | convert::{TryFrom, TryInto}, | ||
5 | ops, | ||
6 | path::{Component, Path, PathBuf}, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | ||
10 | pub struct AbsPathBuf(PathBuf); | ||
11 | |||
12 | impl From<AbsPathBuf> for PathBuf { | ||
13 | fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf { | ||
14 | path_buf | ||
15 | } | ||
16 | } | ||
17 | |||
18 | impl ops::Deref for AbsPathBuf { | ||
19 | type Target = AbsPath; | ||
20 | fn deref(&self) -> &AbsPath { | ||
21 | self.as_path() | ||
22 | } | ||
23 | } | ||
24 | |||
25 | impl AsRef<Path> for AbsPathBuf { | ||
26 | fn as_ref(&self) -> &Path { | ||
27 | self.0.as_path() | ||
28 | } | ||
29 | } | ||
30 | |||
31 | impl AsRef<AbsPath> for AbsPathBuf { | ||
32 | fn as_ref(&self) -> &AbsPath { | ||
33 | self.as_path() | ||
34 | } | ||
35 | } | ||
36 | |||
37 | impl 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 | |||
47 | impl 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 | |||
54 | impl PartialEq<AbsPath> for AbsPathBuf { | ||
55 | fn eq(&self, other: &AbsPath) -> bool { | ||
56 | self.as_path() == other | ||
57 | } | ||
58 | } | ||
59 | |||
60 | impl 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)] | ||
75 | pub struct AbsPath(Path); | ||
76 | |||
77 | impl ops::Deref for AbsPath { | ||
78 | type Target = Path; | ||
79 | fn deref(&self) -> &Path { | ||
80 | &self.0 | ||
81 | } | ||
82 | } | ||
83 | |||
84 | impl AsRef<Path> for AbsPath { | ||
85 | fn as_ref(&self) -> &Path { | ||
86 | &self.0 | ||
87 | } | ||
88 | } | ||
89 | |||
90 | impl<'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 | |||
100 | impl 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)] | ||
124 | pub struct RelPathBuf(PathBuf); | ||
125 | |||
126 | impl From<RelPathBuf> for PathBuf { | ||
127 | fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf { | ||
128 | path_buf | ||
129 | } | ||
130 | } | ||
131 | |||
132 | impl ops::Deref for RelPathBuf { | ||
133 | type Target = RelPath; | ||
134 | fn deref(&self) -> &RelPath { | ||
135 | self.as_path() | ||
136 | } | ||
137 | } | ||
138 | |||
139 | impl AsRef<Path> for RelPathBuf { | ||
140 | fn as_ref(&self) -> &Path { | ||
141 | self.0.as_path() | ||
142 | } | ||
143 | } | ||
144 | |||
145 | impl 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 | |||
155 | impl 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 | |||
162 | impl 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)] | ||
170 | pub struct RelPath(Path); | ||
171 | |||
172 | impl ops::Deref for RelPath { | ||
173 | type Target = Path; | ||
174 | fn deref(&self) -> &Path { | ||
175 | &self.0 | ||
176 | } | ||
177 | } | ||
178 | |||
179 | impl AsRef<Path> for RelPath { | ||
180 | fn as_ref(&self) -> &Path { | ||
181 | &self.0 | ||
182 | } | ||
183 | } | ||
184 | |||
185 | impl 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 | ||
192 | fn 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 | } | ||