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!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430")
252 }
253
254 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
258 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
259 }
260 pub fn starts_with(&self, base: &AbsPath) -> bool {
261 self.0.starts_with(&base.0)
262 }
263 pub fn ends_with(&self, suffix: &RelPath) -> bool {
264 self.0.ends_with(&suffix.0)
265 }
266
267 pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
268 Some((self.file_stem()?, self.extension()))
269 }
270
271 pub fn file_name(&self) -> Option<&str> {
282 self.0.file_name()
283 }
284 pub fn extension(&self) -> Option<&str> {
285 self.0.extension()
286 }
287 pub fn file_stem(&self) -> Option<&str> {
288 self.0.file_stem()
289 }
290 pub fn as_os_str(&self) -> &OsStr {
291 self.0.as_os_str()
292 }
293 pub fn as_str(&self) -> &str {
294 self.0.as_str()
295 }
296 #[deprecated(note = "use Display instead")]
297 pub fn display(&self) -> ! {
298 unimplemented!()
299 }
300 #[deprecated(note = "use std::fs::metadata().is_ok() instead")]
301 pub fn exists(&self) -> ! {
302 unimplemented!()
303 }
304
305 pub fn components(&self) -> Utf8Components<'_> {
306 self.0.components()
307 }
308 }
310
311impl fmt::Display for AbsPath {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 fmt::Display::fmt(&self.0, f)
314 }
315}
316
317#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
319pub struct RelPathBuf(Utf8PathBuf);
320
321impl From<RelPathBuf> for Utf8PathBuf {
322 fn from(RelPathBuf(path_buf): RelPathBuf) -> Utf8PathBuf {
323 path_buf
324 }
325}
326
327impl ops::Deref for RelPathBuf {
328 type Target = RelPath;
329 fn deref(&self) -> &RelPath {
330 self.as_path()
331 }
332}
333
334impl AsRef<Utf8Path> for RelPathBuf {
335 fn as_ref(&self) -> &Utf8Path {
336 self.0.as_path()
337 }
338}
339
340impl AsRef<Path> for RelPathBuf {
341 fn as_ref(&self) -> &Path {
342 self.0.as_ref()
343 }
344}
345
346impl TryFrom<Utf8PathBuf> for RelPathBuf {
347 type Error = Utf8PathBuf;
348 fn try_from(path_buf: Utf8PathBuf) -> Result<RelPathBuf, Utf8PathBuf> {
349 if !path_buf.is_relative() {
350 return Err(path_buf);
351 }
352 Ok(RelPathBuf(path_buf))
353 }
354}
355
356impl TryFrom<&str> for RelPathBuf {
357 type Error = Utf8PathBuf;
358 fn try_from(path: &str) -> Result<RelPathBuf, Utf8PathBuf> {
359 RelPathBuf::try_from(Utf8PathBuf::from(path))
360 }
361}
362
363impl RelPathBuf {
364 pub fn as_path(&self) -> &RelPath {
368 RelPath::new_unchecked(self.0.as_path())
369 }
370}
371
372#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
374#[repr(transparent)]
375pub struct RelPath(Utf8Path);
376
377impl AsRef<Utf8Path> for RelPath {
378 fn as_ref(&self) -> &Utf8Path {
379 &self.0
380 }
381}
382
383impl AsRef<Path> for RelPath {
384 fn as_ref(&self) -> &Path {
385 self.0.as_ref()
386 }
387}
388
389impl RelPath {
390 pub fn new_unchecked(path: &Utf8Path) -> &RelPath {
392 unsafe { &*(path as *const Utf8Path as *const RelPath) }
393 }
394
395 pub fn to_path_buf(&self) -> RelPathBuf {
397 RelPathBuf::try_from(self.0.to_path_buf()).unwrap()
398 }
399
400 pub fn as_utf8_path(&self) -> &Utf8Path {
401 self.as_ref()
402 }
403
404 pub fn as_str(&self) -> &str {
405 self.0.as_str()
406 }
407}
408
409fn normalize_path(path: &Utf8Path) -> Utf8PathBuf {
411 let mut components = path.components().peekable();
412 let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().copied() {
413 components.next();
414 Utf8PathBuf::from(c.as_str())
415 } else {
416 Utf8PathBuf::new()
417 };
418
419 for component in components {
420 match component {
421 Utf8Component::Prefix(..) => unreachable!(),
422 Utf8Component::RootDir => {
423 ret.push(component.as_str());
424 }
425 Utf8Component::CurDir => {}
426 Utf8Component::ParentDir => {
427 ret.pop();
428 }
429 Utf8Component::Normal(c) => {
430 ret.push(c);
431 }
432 }
433 }
434 ret
435}