diff options
Diffstat (limited to 'crates/paths/src/lib.rs')
-rw-r--r-- | crates/paths/src/lib.rs | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 1b259682d..22011cb33 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -6,6 +6,7 @@ use std::{ | |||
6 | path::{Component, Path, PathBuf}, | 6 | path::{Component, Path, PathBuf}, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | /// Wrapper around an absolute [`PathBuf`]. | ||
9 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 10 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
10 | pub struct AbsPathBuf(PathBuf); | 11 | pub struct AbsPathBuf(PathBuf); |
11 | 12 | ||
@@ -58,18 +59,33 @@ impl PartialEq<AbsPath> for AbsPathBuf { | |||
58 | } | 59 | } |
59 | 60 | ||
60 | impl AbsPathBuf { | 61 | impl AbsPathBuf { |
62 | /// Wrap the given absolute path in `AbsPathBuf` | ||
63 | /// | ||
64 | /// # Panics | ||
65 | /// | ||
66 | /// Panics if `path` is not absolute. | ||
61 | pub fn assert(path: PathBuf) -> AbsPathBuf { | 67 | pub fn assert(path: PathBuf) -> AbsPathBuf { |
62 | AbsPathBuf::try_from(path) | 68 | AbsPathBuf::try_from(path) |
63 | .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) | 69 | .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) |
64 | } | 70 | } |
71 | |||
72 | /// Coerces to a `AbsPath` slice. | ||
73 | /// | ||
74 | /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`. | ||
65 | pub fn as_path(&self) -> &AbsPath { | 75 | pub fn as_path(&self) -> &AbsPath { |
66 | AbsPath::assert(self.0.as_path()) | 76 | AbsPath::assert(self.0.as_path()) |
67 | } | 77 | } |
78 | |||
79 | /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`. | ||
80 | /// | ||
81 | /// Note that this won't remove the root component, so `self` will still be | ||
82 | /// absolute. | ||
68 | pub fn pop(&mut self) -> bool { | 83 | pub fn pop(&mut self) -> bool { |
69 | self.0.pop() | 84 | self.0.pop() |
70 | } | 85 | } |
71 | } | 86 | } |
72 | 87 | ||
88 | /// Wrapper around an absolute [`Path`]. | ||
73 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 89 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
74 | #[repr(transparent)] | 90 | #[repr(transparent)] |
75 | pub struct AbsPath(Path); | 91 | pub struct AbsPath(Path); |
@@ -98,28 +114,56 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath { | |||
98 | } | 114 | } |
99 | 115 | ||
100 | impl AbsPath { | 116 | impl AbsPath { |
117 | /// Wrap the given absolute path in `AbsPath` | ||
118 | /// | ||
119 | /// # Panics | ||
120 | /// | ||
121 | /// Panics if `path` is not absolute. | ||
101 | pub fn assert(path: &Path) -> &AbsPath { | 122 | pub fn assert(path: &Path) -> &AbsPath { |
102 | assert!(path.is_absolute()); | 123 | assert!(path.is_absolute()); |
103 | unsafe { &*(path as *const Path as *const AbsPath) } | 124 | unsafe { &*(path as *const Path as *const AbsPath) } |
104 | } | 125 | } |
105 | 126 | ||
127 | /// Equivalent of [`Path::parent`] for `AbsPath`. | ||
106 | pub fn parent(&self) -> Option<&AbsPath> { | 128 | pub fn parent(&self) -> Option<&AbsPath> { |
107 | self.0.parent().map(AbsPath::assert) | 129 | self.0.parent().map(AbsPath::assert) |
108 | } | 130 | } |
131 | |||
132 | /// Equivalent of [`Path::join`] for `AbsPath`. | ||
109 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { | 133 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { |
110 | self.as_ref().join(path).try_into().unwrap() | 134 | self.as_ref().join(path).try_into().unwrap() |
111 | } | 135 | } |
136 | |||
137 | /// Normalize the given path: | ||
138 | /// - Removes repeated separators: `/a//b` becomes `/a/b` | ||
139 | /// - Removes occurrences of `.` and resolves `..`. | ||
140 | /// - Removes trailing slashes: `/a/b/` becomes `/a/b`. | ||
141 | /// | ||
142 | /// # Example | ||
143 | /// ``` | ||
144 | /// # use paths::AbsPathBuf; | ||
145 | /// let abs_path_buf = AbsPathBuf::assert("/a/../../b/.//c//".into()); | ||
146 | /// let normalized = abs_path_buf.normalize(); | ||
147 | /// assert_eq!(normalized, AbsPathBuf::assert("/b/c".into())); | ||
148 | /// ``` | ||
112 | pub fn normalize(&self) -> AbsPathBuf { | 149 | pub fn normalize(&self) -> AbsPathBuf { |
113 | AbsPathBuf(normalize_path(&self.0)) | 150 | AbsPathBuf(normalize_path(&self.0)) |
114 | } | 151 | } |
152 | |||
153 | /// Equivalent of [`Path::to_path_buf`] for `AbsPath`. | ||
115 | pub fn to_path_buf(&self) -> AbsPathBuf { | 154 | pub fn to_path_buf(&self) -> AbsPathBuf { |
116 | AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() | 155 | AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() |
117 | } | 156 | } |
157 | |||
158 | /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. | ||
159 | /// | ||
160 | /// Returns a relative path. | ||
118 | pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { | 161 | pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { |
119 | self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) | 162 | self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) |
120 | } | 163 | } |
121 | } | 164 | } |
122 | 165 | ||
166 | /// Wrapper around a relative [`PathBuf`]. | ||
123 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 167 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
124 | pub struct RelPathBuf(PathBuf); | 168 | pub struct RelPathBuf(PathBuf); |
125 | 169 | ||
@@ -160,11 +204,15 @@ impl TryFrom<&str> for RelPathBuf { | |||
160 | } | 204 | } |
161 | 205 | ||
162 | impl RelPathBuf { | 206 | impl RelPathBuf { |
207 | /// Coerces to a `RelPath` slice. | ||
208 | /// | ||
209 | /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`. | ||
163 | pub fn as_path(&self) -> &RelPath { | 210 | pub fn as_path(&self) -> &RelPath { |
164 | RelPath::new_unchecked(self.0.as_path()) | 211 | RelPath::new_unchecked(self.0.as_path()) |
165 | } | 212 | } |
166 | } | 213 | } |
167 | 214 | ||
215 | /// Wrapper around a relative [`Path`]. | ||
168 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 216 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
169 | #[repr(transparent)] | 217 | #[repr(transparent)] |
170 | pub struct RelPath(Path); | 218 | pub struct RelPath(Path); |
@@ -183,12 +231,13 @@ impl AsRef<Path> for RelPath { | |||
183 | } | 231 | } |
184 | 232 | ||
185 | impl RelPath { | 233 | impl RelPath { |
234 | /// Creates a new `RelPath` from `path`, without checking if it is relative. | ||
186 | pub fn new_unchecked(path: &Path) -> &RelPath { | 235 | pub fn new_unchecked(path: &Path) -> &RelPath { |
187 | unsafe { &*(path as *const Path as *const RelPath) } | 236 | unsafe { &*(path as *const Path as *const RelPath) } |
188 | } | 237 | } |
189 | } | 238 | } |
190 | 239 | ||
191 | // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 | 240 | /// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 |
192 | fn normalize_path(path: &Path) -> PathBuf { | 241 | fn normalize_path(path: &Path) -> PathBuf { |
193 | let mut components = path.components().peekable(); | 242 | let mut components = path.components().peekable(); |
194 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { | 243 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { |