diff options
author | Aleksey Kladov <[email protected]> | 2020-06-14 13:08:28 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-06-14 13:15:36 +0100 |
commit | a32cff333dd34b7db886317470f1301f0266b9e7 (patch) | |
tree | 7531c71ea8bf3b38d28c6ec51be8141ad9384dc0 | |
parent | 246c66a7f7fd3f85d7d6e47a36f17bafb0ccb08a (diff) |
Introduce paths crate
It's a good idea to distinguish between absolute and relative paths at
the type level, to avoid accidental dependency on the cwd, which
really shouldn't matter for rust-analyzer service
-rw-r--r-- | Cargo.lock | 4 | ||||
-rw-r--r-- | crates/paths/Cargo.toml | 8 | ||||
-rw-r--r-- | crates/paths/src/lib.rs | 123 |
3 files changed, 135 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock index 308e36836..5848e61c7 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -835,6 +835,10 @@ dependencies = [ | |||
835 | ] | 835 | ] |
836 | 836 | ||
837 | [[package]] | 837 | [[package]] |
838 | name = "paths" | ||
839 | version = "0.1.0" | ||
840 | |||
841 | [[package]] | ||
838 | name = "percent-encoding" | 842 | name = "percent-encoding" |
839 | version = "2.1.0" | 843 | version = "2.1.0" |
840 | source = "registry+https://github.com/rust-lang/crates.io-index" | 844 | source = "registry+https://github.com/rust-lang/crates.io-index" |
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml new file mode 100644 index 000000000..646ee7fd5 --- /dev/null +++ b/crates/paths/Cargo.toml | |||
@@ -0,0 +1,8 @@ | |||
1 | [package] | ||
2 | name = "paths" | ||
3 | version = "0.1.0" | ||
4 | authors = ["rust-analyzer developers"] | ||
5 | edition = "2018" | ||
6 | |||
7 | [lib] | ||
8 | doctest = false | ||
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs new file mode 100644 index 000000000..c7ce0c42f --- /dev/null +++ b/crates/paths/src/lib.rs | |||
@@ -0,0 +1,123 @@ | |||
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 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 as_path(&self) -> &AbsPath { | ||
50 | AbsPath::new_unchecked(self.0.as_path()) | ||
51 | } | ||
52 | pub fn pop(&mut self) -> bool { | ||
53 | self.0.pop() | ||
54 | } | ||
55 | } | ||
56 | |||
57 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | ||
58 | #[repr(transparent)] | ||
59 | pub struct AbsPath(Path); | ||
60 | |||
61 | impl ops::Deref for AbsPath { | ||
62 | type Target = Path; | ||
63 | fn deref(&self) -> &Path { | ||
64 | &self.0 | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl AsRef<Path> for AbsPath { | ||
69 | fn as_ref(&self) -> &Path { | ||
70 | &self.0 | ||
71 | } | ||
72 | } | ||
73 | |||
74 | impl<'a> TryFrom<&'a Path> for &'a AbsPath { | ||
75 | type Error = &'a Path; | ||
76 | fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { | ||
77 | if !path.is_absolute() { | ||
78 | return Err(path); | ||
79 | } | ||
80 | Ok(AbsPath::new_unchecked(path)) | ||
81 | } | ||
82 | } | ||
83 | |||
84 | impl AbsPath { | ||
85 | fn new_unchecked(path: &Path) -> &AbsPath { | ||
86 | unsafe { &*(path as *const Path as *const AbsPath) } | ||
87 | } | ||
88 | |||
89 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { | ||
90 | self.as_ref().join(path).try_into().unwrap() | ||
91 | } | ||
92 | pub fn normalize(&self) -> AbsPathBuf { | ||
93 | AbsPathBuf(normalize_path(&self.0)) | ||
94 | } | ||
95 | } | ||
96 | |||
97 | // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 | ||
98 | fn normalize_path(path: &Path) -> PathBuf { | ||
99 | let mut components = path.components().peekable(); | ||
100 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { | ||
101 | components.next(); | ||
102 | PathBuf::from(c.as_os_str()) | ||
103 | } else { | ||
104 | PathBuf::new() | ||
105 | }; | ||
106 | |||
107 | for component in components { | ||
108 | match component { | ||
109 | Component::Prefix(..) => unreachable!(), | ||
110 | Component::RootDir => { | ||
111 | ret.push(component.as_os_str()); | ||
112 | } | ||
113 | Component::CurDir => {} | ||
114 | Component::ParentDir => { | ||
115 | ret.pop(); | ||
116 | } | ||
117 | Component::Normal(c) => { | ||
118 | ret.push(c); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | ret | ||
123 | } | ||