]> git.lizzy.rs Git - rust.git/blob - crates/paths/src/lib.rs
Merge #4983
[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     pub fn to_path_buf(&self) -> AbsPathBuf {
99         AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
100     }
101     pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
102         self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
103     }
104 }
105
106 #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
107 pub struct RelPathBuf(PathBuf);
108
109 impl From<RelPathBuf> for PathBuf {
110     fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf {
111         path_buf
112     }
113 }
114
115 impl ops::Deref for RelPathBuf {
116     type Target = RelPath;
117     fn deref(&self) -> &RelPath {
118         self.as_path()
119     }
120 }
121
122 impl AsRef<Path> for RelPathBuf {
123     fn as_ref(&self) -> &Path {
124         self.0.as_path()
125     }
126 }
127
128 impl TryFrom<PathBuf> for RelPathBuf {
129     type Error = PathBuf;
130     fn try_from(path_buf: PathBuf) -> Result<RelPathBuf, PathBuf> {
131         if !path_buf.is_relative() {
132             return Err(path_buf);
133         }
134         Ok(RelPathBuf(path_buf))
135     }
136 }
137
138 impl TryFrom<&str> for RelPathBuf {
139     type Error = PathBuf;
140     fn try_from(path: &str) -> Result<RelPathBuf, PathBuf> {
141         RelPathBuf::try_from(PathBuf::from(path))
142     }
143 }
144
145 impl RelPathBuf {
146     pub fn as_path(&self) -> &RelPath {
147         RelPath::new_unchecked(self.0.as_path())
148     }
149 }
150
151 #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
152 #[repr(transparent)]
153 pub struct RelPath(Path);
154
155 impl ops::Deref for RelPath {
156     type Target = Path;
157     fn deref(&self) -> &Path {
158         &self.0
159     }
160 }
161
162 impl AsRef<Path> for RelPath {
163     fn as_ref(&self) -> &Path {
164         &self.0
165     }
166 }
167
168 impl RelPath {
169     pub fn new_unchecked(path: &Path) -> &RelPath {
170         unsafe { &*(path as *const Path as *const RelPath) }
171     }
172 }
173
174 // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
175 fn normalize_path(path: &Path) -> PathBuf {
176     let mut components = path.components().peekable();
177     let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
178         components.next();
179         PathBuf::from(c.as_os_str())
180     } else {
181         PathBuf::new()
182     };
183
184     for component in components {
185         match component {
186             Component::Prefix(..) => unreachable!(),
187             Component::RootDir => {
188                 ret.push(component.as_os_str());
189             }
190             Component::CurDir => {}
191             Component::ParentDir => {
192                 ret.pop();
193             }
194             Component::Normal(c) => {
195                 ret.push(c);
196             }
197         }
198     }
199     ret
200 }