diff options
Diffstat (limited to 'crates/vfs/src/vfs_path.rs')
-rw-r--r-- | crates/vfs/src/vfs_path.rs | 55 |
1 files changed, 53 insertions, 2 deletions
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index 74b6333e2..2b3d7fd84 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -102,7 +102,14 @@ impl VfsPath { | |||
102 | } | 102 | } |
103 | } | 103 | } |
104 | 104 | ||
105 | // Don't make this `pub` | 105 | /// **Don't make this `pub`** |
106 | /// | ||
107 | /// Encode the path in the given buffer. | ||
108 | /// | ||
109 | /// The encoding will be `0` if [`AbsPathBuf`], `1` if [`VirtualPath`], followed | ||
110 | /// by `self`'s representation. | ||
111 | /// | ||
112 | /// Note that this encoding is dependent on the operating system. | ||
106 | pub(crate) fn encode(&self, buf: &mut Vec<u8>) { | 113 | pub(crate) fn encode(&self, buf: &mut Vec<u8>) { |
107 | let tag = match &self.0 { | 114 | let tag = match &self.0 { |
108 | VfsPathRepr::PathBuf(_) => 0, | 115 | VfsPathRepr::PathBuf(_) => 0, |
@@ -259,6 +266,7 @@ mod windows_paths { | |||
259 | } | 266 | } |
260 | } | 267 | } |
261 | 268 | ||
269 | /// Internal, private representation of [`VfsPath`]. | ||
262 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 270 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
263 | enum VfsPathRepr { | 271 | enum VfsPathRepr { |
264 | PathBuf(AbsPathBuf), | 272 | PathBuf(AbsPathBuf), |
@@ -295,13 +303,34 @@ impl fmt::Debug for VfsPathRepr { | |||
295 | } | 303 | } |
296 | } | 304 | } |
297 | 305 | ||
306 | /// `/`-separated virtual path. | ||
307 | /// | ||
308 | /// This is used to describe files that do not reside on the file system. | ||
298 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 309 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
299 | struct VirtualPath(String); | 310 | struct VirtualPath(String); |
300 | 311 | ||
301 | impl VirtualPath { | 312 | impl VirtualPath { |
313 | /// Returns `true` if `other` is a prefix of `self` (as strings). | ||
302 | fn starts_with(&self, other: &VirtualPath) -> bool { | 314 | fn starts_with(&self, other: &VirtualPath) -> bool { |
303 | self.0.starts_with(&other.0) | 315 | self.0.starts_with(&other.0) |
304 | } | 316 | } |
317 | |||
318 | /// Remove the last component of `self`. | ||
319 | /// | ||
320 | /// This will find the last `'/'` in `self`, and remove everything after it, | ||
321 | /// including the `'/'`. | ||
322 | /// | ||
323 | /// If `self` contains no `'/'`, returns `false`; else returns `true`. | ||
324 | /// | ||
325 | /// # Example | ||
326 | /// | ||
327 | /// ```rust,ignore | ||
328 | /// let mut path = VirtualPath("/foo/bar".to_string()); | ||
329 | /// path.pop(); | ||
330 | /// assert_eq!(path.0, "/foo"); | ||
331 | /// path.pop(); | ||
332 | /// assert_eq!(path.0, ""); | ||
333 | /// ``` | ||
305 | fn pop(&mut self) -> bool { | 334 | fn pop(&mut self) -> bool { |
306 | let pos = match self.0.rfind('/') { | 335 | let pos = match self.0.rfind('/') { |
307 | Some(pos) => pos, | 336 | Some(pos) => pos, |
@@ -310,6 +339,17 @@ impl VirtualPath { | |||
310 | self.0 = self.0[..pos].to_string(); | 339 | self.0 = self.0[..pos].to_string(); |
311 | true | 340 | true |
312 | } | 341 | } |
342 | |||
343 | /// Append the given *relative* path `path` to `self`. | ||
344 | /// | ||
345 | /// This will resolve any leading `"../"` in `path` before appending it. | ||
346 | /// | ||
347 | /// Returns [`None`] if `path` has more leading `"../"` than the number of | ||
348 | /// components in `self`. | ||
349 | /// | ||
350 | /// # Notes | ||
351 | /// | ||
352 | /// In practice, appending here means `self/path` as strings. | ||
313 | fn join(&self, mut path: &str) -> Option<VirtualPath> { | 353 | fn join(&self, mut path: &str) -> Option<VirtualPath> { |
314 | let mut res = self.clone(); | 354 | let mut res = self.clone(); |
315 | while path.starts_with("../") { | 355 | while path.starts_with("../") { |
@@ -322,7 +362,18 @@ impl VirtualPath { | |||
322 | Some(res) | 362 | Some(res) |
323 | } | 363 | } |
324 | 364 | ||
325 | pub(crate) fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { | 365 | /// Returns `self`'s base name and file extension. |
366 | /// | ||
367 | /// # Returns | ||
368 | /// - `None` if `self` ends with `"//"`. | ||
369 | /// - `Some((name, None))` if `self`'s base contains no `.`, or only one `.` at | ||
370 | /// the start. | ||
371 | /// - `Some((name, Some(extension))` else. | ||
372 | /// | ||
373 | /// # Note | ||
374 | /// The extension will not contains `.`. This means `"/foo/bar.baz.rs"` will | ||
375 | /// return `Some(("bar.baz", Some("rs"))`. | ||
376 | fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { | ||
326 | let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 }; | 377 | let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 }; |
327 | let file_name = match file_path.rfind('/') { | 378 | let file_name = match file_path.rfind('/') { |
328 | Some(position) => &file_path[position + 1..], | 379 | Some(position) => &file_path[position + 1..], |