diff options
Diffstat (limited to 'crates/paths')
-rw-r--r-- | crates/paths/Cargo.toml | 8 | ||||
-rw-r--r-- | crates/paths/src/lib.rs | 126 |
2 files changed, 134 insertions, 0 deletions
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..190c50913 --- /dev/null +++ b/crates/paths/src/lib.rs | |||
@@ -0,0 +1,126 @@ | |||
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 | } | ||
99 | |||
100 | // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 | ||
101 | fn normalize_path(path: &Path) -> PathBuf { | ||
102 | let mut components = path.components().peekable(); | ||
103 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { | ||
104 | components.next(); | ||
105 | PathBuf::from(c.as_os_str()) | ||
106 | } else { | ||
107 | PathBuf::new() | ||
108 | }; | ||
109 | |||
110 | for component in components { | ||
111 | match component { | ||
112 | Component::Prefix(..) => unreachable!(), | ||
113 | Component::RootDir => { | ||
114 | ret.push(component.as_os_str()); | ||
115 | } | ||
116 | Component::CurDir => {} | ||
117 | Component::ParentDir => { | ||
118 | ret.pop(); | ||
119 | } | ||
120 | Component::Normal(c) => { | ||
121 | ret.push(c); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | ret | ||
126 | } | ||