1use std::{
5 borrow::Borrow,
6 ffi::OsStr,
7 fmt, ops,
8 path::{Path, PathBuf},
9};
10
11pub use camino::{Utf8Component, Utf8Components, Utf8Path, Utf8PathBuf, Utf8Prefix};
12
13#[derive(Debug, Clone, Ord, PartialOrd, Eq, Hash)]
15pub struct AbsPathBuf(Utf8PathBuf);
16
17impl From<AbsPathBuf> for Utf8PathBuf {
18 fn from(AbsPathBuf(path_buf): AbsPathBuf) -> Utf8PathBuf {
19 path_buf
20 }
21}
22
23impl From<AbsPathBuf> for PathBuf {
24 fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf {
25 path_buf.into()
26 }
27}
28
29impl ops::Deref for AbsPathBuf {
30 type Target = AbsPath;
31 fn deref(&self) -> &AbsPath {
32 self.as_path()
33 }
34}
35
36impl AsRef<Utf8Path> for AbsPathBuf {
37 fn as_ref(&self) -> &Utf8Path {
38 self.0.as_path()
39 }
40}
41
42impl AsRef<OsStr> for AbsPathBuf {
43 fn as_ref(&self) -> &OsStr {
44 self.0.as_ref()
45 }
46}
47
48impl AsRef<Path> for AbsPathBuf {
49 fn as_ref(&self) -> &Path {
50 self.0.as_ref()
51 }
52}
53
54impl AsRef<AbsPath> for AbsPathBuf {
55 fn as_ref(&self) -> &AbsPath {
56 self.as_path()
57 }
58}
59
60impl Borrow<AbsPath> for AbsPathBuf {
61 fn borrow(&self) -> &AbsPath {
62 self.as_path()
63 }
64}
65
66impl TryFrom<Utf8PathBuf> for AbsPathBuf {
67 type Error = Utf8PathBuf;
68 fn try_from(path_buf: Utf8PathBuf) -> Result<AbsPathBuf, Utf8PathBuf> {
69 if !path_buf.is_absolute() {
70 return Err(path_buf);
71 }
72 Ok(AbsPathBuf(path_buf))
73 }
74}
75
76impl TryFrom<&str> for AbsPathBuf {
77 type Error = Utf8PathBuf;
78 fn try_from(path: &str) -> Result<AbsPathBuf, Utf8PathBuf> {
79 AbsPathBuf::try_from(Utf8PathBuf::from(path))
80 }
81}
82
83impl<P: AsRef<Path> + ?Sized> PartialEq<P> for AbsPathBuf {
84 fn eq(&self, other: &P) -> bool {
85 self.0.as_std_path() == other.as_ref()
86 }
87}
88
89impl AbsPathBuf {
90 pub fn assert(path: Utf8PathBuf) -> AbsPathBuf {
96 AbsPathBuf::try_from(path)
97 .unwrap_or_else(|path| panic!("expected absolute path, got {path}"))
98 }
99
100 pub fn assert_utf8(path: PathBuf) -> AbsPathBuf {
106 AbsPathBuf::assert(
107 Utf8PathBuf::from_path_buf(path)
108 .unwrap_or_else(|path| panic!("expected utf8 path, got {}", path.display())),
109 )
110 }
111
112 pub fn as_path(&self) -> &AbsPath {
116 AbsPath::assert(self.0.as_path())
117 }
118
119 pub fn pop(&mut self) -> bool {
124 self.0.pop()
125 }
126
127 pub fn push<P: AsRef<Utf8Path>>(&mut self, suffix: P) {
142 self.0.push(suffix)
143 }
144
145 pub fn join(&self, path: impl AsRef<Utf8Path>) -> Self {
146 Self(self.0.join(path))
147 }
148}
149
150impl fmt::Display for AbsPathBuf {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 fmt::Display::fmt(&self.0, f)
153 }
154}
155
156#[derive(Debug, Ord, PartialOrd, Eq, Hash)]
158#[repr(transparent)]
159pub struct AbsPath(Utf8Path);
160
161impl<P: AsRef<Path> + ?Sized> PartialEq<P> for AbsPath {
162 fn eq(&self, other: &P) -> bool {
163 self.0.as_std_path() == other.as_ref()
164 }
165}
166
167impl AsRef<Utf8Path> for AbsPath {
168 fn as_ref(&self) -> &Utf8Path {
169 &self.0
170 }
171}
172
173impl AsRef<Path> for AbsPath {
174 fn as_ref(&self) -> &Path {
175 self.0.as_ref()
176 }
177}
178
179impl AsRef<OsStr> for AbsPath {
180 fn as_ref(&self) -> &OsStr {
181 self.0.as_ref()
182 }
183}
184
185impl ToOwned for AbsPath {
186 type Owned = AbsPathBuf;
187
188 fn to_owned(&self) -> Self::Owned {
189 AbsPathBuf(self.0.to_owned())
190 }
191}
192
193impl<'a> TryFrom<&'a Utf8Path> for &'a AbsPath {
194 type Error = &'a Utf8Path;
195 fn try_from(path: &'a Utf8Path) -> Result<&'a AbsPath, &'a Utf8Path> {
196 if !path.is_absolute() {
197 return Err(path);
198 }
199 Ok(AbsPath::assert(path))
200 }
201}
202
203impl AbsPath {
204 pub fn assert(path: &Utf8Path) -> &AbsPath {
210 assert!(path.is_absolute(), "{path} is not absolute");
211 unsafe { &*(path as *const Utf8Path as *const AbsPath) }
212 }
213
214 pub fn parent(&self) -> Option<&AbsPath> {
216 self.0.parent().map(AbsPath::assert)
217 }
218
219 pub fn absolutize(&self, path: impl AsRef<Utf8Path>) -> AbsPathBuf {
221 self.join(path).normalize()
222 }
223
224 pub fn join(&self, path: impl AsRef<Utf8Path>) -> AbsPathBuf {
226 Utf8Path::join(self.as_ref(), path).try_into().unwrap()
227 }
228
229 pub fn normalize(&self) -> AbsPathBuf {
242 AbsPathBuf(normalize_path(&self.0))
243 }
244
245 pub fn to_path_buf(&self) -> AbsPathBuf {
247 AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
248 }
249
250 pub fn canonicalize(&self) -> ! {
251 panic!(
252 "We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430"
253 )
254 }
255
256 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
260 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
261 }
262 pub fn starts_with(&self, base: &AbsPath) -> bool {
263 self.0.starts_with(&base.0)
264 }
265 pub fn ends_with(&self, suffix: &RelPath) -> bool {
266 self.0.ends_with(&suffix.0)
267 }
268
269 pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
270 Some((self.file_stem()?, self.extension()))
271 }
272
273 pub fn file_name(&self) -> Option<&str> {
284 self.0.file_name()
285 }
286 pub fn extension(&self) -> Option<&str> {
287 self.0.extension()
288 }
289 pub fn file_stem(&self) -> Option<&str> {
290 self.0.file_stem()
291 }
292 pub fn as_os_str(&self) -> &OsStr {
293 self.0.as_os_str()
294 }
295 pub fn as_str(&self) -> &str {
296 self.0.as_str()
297 }
298 #[deprecated(note = "use Display instead")]
299 pub fn display(&self) -> ! {
300 unimplemented!()
301 }
302 #[deprecated(note = "use std::fs::metadata().is_ok() instead")]
303 pub fn exists(&self) -> ! {
304 unimplemented!()
305 }
306
307 pub fn components(&self) -> Utf8Components<'_> {
308 self.0.components()
309 }
310 }
312
313impl fmt::Display for AbsPath {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 fmt::Display::fmt(&self.0, f)
316 }
317}
318
319#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
321pub struct RelPathBuf(Utf8PathBuf);
322
323impl From<RelPathBuf> for Utf8PathBuf {
324 fn from(RelPathBuf(path_buf): RelPathBuf) -> Utf8PathBuf {
325 path_buf
326 }
327}
328
329impl ops::Deref for RelPathBuf {
330 type Target = RelPath;
331 fn deref(&self) -> &RelPath {
332 self.as_path()
333 }
334}
335
336impl AsRef<Utf8Path> for RelPathBuf {
337 fn as_ref(&self) -> &Utf8Path {
338 self.0.as_path()
339 }
340}
341
342impl AsRef<Path> for RelPathBuf {
343 fn as_ref(&self) -> &Path {
344 self.0.as_ref()
345 }
346}
347
348impl TryFrom<Utf8PathBuf> for RelPathBuf {
349 type Error = Utf8PathBuf;
350 fn try_from(path_buf: Utf8PathBuf) -> Result<RelPathBuf, Utf8PathBuf> {
351 if !path_buf.is_relative() {
352 return Err(path_buf);
353 }
354 Ok(RelPathBuf(path_buf))
355 }
356}
357
358impl TryFrom<&str> for RelPathBuf {
359 type Error = Utf8PathBuf;
360 fn try_from(path: &str) -> Result<RelPathBuf, Utf8PathBuf> {
361 RelPathBuf::try_from(Utf8PathBuf::from(path))
362 }
363}
364
365impl RelPathBuf {
366 pub fn as_path(&self) -> &RelPath {
370 RelPath::new_unchecked(self.0.as_path())
371 }
372}
373
374#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
376#[repr(transparent)]
377pub struct RelPath(Utf8Path);
378
379impl AsRef<Utf8Path> for RelPath {
380 fn as_ref(&self) -> &Utf8Path {
381 &self.0
382 }
383}
384
385impl AsRef<Path> for RelPath {
386 fn as_ref(&self) -> &Path {
387 self.0.as_ref()
388 }
389}
390
391impl RelPath {
392 pub fn new_unchecked(path: &Utf8Path) -> &RelPath {
394 unsafe { &*(path as *const Utf8Path as *const RelPath) }
395 }
396
397 pub fn to_path_buf(&self) -> RelPathBuf {
399 RelPathBuf::try_from(self.0.to_path_buf()).unwrap()
400 }
401
402 pub fn as_utf8_path(&self) -> &Utf8Path {
403 self.as_ref()
404 }
405
406 pub fn as_str(&self) -> &str {
407 self.0.as_str()
408 }
409}
410
411fn normalize_path(path: &Utf8Path) -> Utf8PathBuf {
413 let mut components = path.components().peekable();
414 let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().copied() {
415 components.next();
416 Utf8PathBuf::from(c.as_str())
417 } else {
418 Utf8PathBuf::new()
419 };
420
421 for component in components {
422 match component {
423 Utf8Component::Prefix(..) => unreachable!(),
424 Utf8Component::RootDir => {
425 ret.push(component.as_str());
426 }
427 Utf8Component::CurDir => {}
428 Utf8Component::ParentDir => {
429 ret.pop();
430 }
431 Utf8Component::Normal(c) => {
432 ret.push(c);
433 }
434 }
435 }
436 ret
437}