]> git.lizzy.rs Git - rust.git/blob - crates/paths/src/lib.rs
Merge #4937
[rust.git] / crates / paths / src / lib.rs
1 //! Thin wrappers around `std::path`, distinguishing between absolute and
2 //! relative paths.
3 use std::{
4     convert::{TryFrom, TryInto},
5     io, 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 TryFrom<PathBuf> for AbsPathBuf {
32     type Error = PathBuf;
33     fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
34         if !path_buf.is_absolute() {
35             return Err(path_buf);
36         }
37         Ok(AbsPathBuf(path_buf))
38     }
39 }
40
41 impl TryFrom<&str> for AbsPathBuf {
42     type Error = PathBuf;
43     fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> {
44         AbsPathBuf::try_from(PathBuf::from(path))
45     }
46 }
47
48 impl AbsPathBuf {
49     pub fn canonicalized(path: &Path) -> io::Result<AbsPathBuf> {
50         path.canonicalize().map(|it| AbsPathBuf::try_from(it).unwrap())
51     }
52     pub fn as_path(&self) -> &AbsPath {
53         AbsPath::new_unchecked(self.0.as_path())
54     }
55     pub fn pop(&mut self) -> bool {
56         self.0.pop()
57     }
58 }
59
60 #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
61 #[repr(transparent)]
62 pub struct AbsPath(Path);
63
64 impl ops::Deref for AbsPath {
65     type Target = Path;
66     fn deref(&self) -> &Path {
67         &self.0
68     }
69 }
70
71 impl AsRef<Path> for AbsPath {
72     fn as_ref(&self) -> &Path {
73         &self.0
74     }
75 }
76
77 impl<'a> TryFrom<&'a Path> for &'a AbsPath {
78     type Error = &'a Path;
79     fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
80         if !path.is_absolute() {
81             return Err(path);
82         }
83         Ok(AbsPath::new_unchecked(path))
84     }
85 }
86
87 impl AbsPath {
88     fn new_unchecked(path: &Path) -> &AbsPath {
89         unsafe { &*(path as *const Path as *const AbsPath) }
90     }
91
92     pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
93         self.as_ref().join(path).try_into().unwrap()
94     }
95     pub fn normalize(&self) -> AbsPathBuf {
96         AbsPathBuf(normalize_path(&self.0))
97     }
98 }
99
100 // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
101 fn normalize_path(path: &Path) -> PathBuf {
102     let mut components = path.components().peekable();
103     let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
104         components.next();
105         PathBuf::from(c.as_os_str())
106     } else {
107         PathBuf::new()
108     };
109
110     for component in components {
111         match component {
112             Component::Prefix(..) => unreachable!(),
113             Component::RootDir => {
114                 ret.push(component.as_os_str());
115             }
116             Component::CurDir => {}
117             Component::ParentDir => {
118                 ret.pop();
119             }
120             Component::Normal(c) => {
121                 ret.push(c);
122             }
123         }
124     }
125     ret
126 }