]> git.lizzy.rs Git - rust.git/blob - crates/paths/src/lib.rs
Merge #4676
[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     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 }