aboutsummaryrefslogtreecommitdiff
path: root/crates/vfs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/vfs')
-rw-r--r--crates/vfs/Cargo.toml4
-rw-r--r--crates/vfs/src/vfs_path.rs139
2 files changed, 134 insertions, 9 deletions
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
index b74cdb7ff..9ae8f19b6 100644
--- a/crates/vfs/Cargo.toml
+++ b/crates/vfs/Cargo.toml
@@ -1,9 +1,9 @@
1[package] 1[package]
2name = "vfs" 2name = "vfs"
3version = "0.1.0" 3version = "0.0.0"
4license = "MIT OR Apache-2.0"
4authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
5edition = "2018" 6edition = "2018"
6license = "MIT OR Apache-2.0"
7 7
8[lib] 8[lib]
9doctest = false 9doctest = false
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
index 04a42264e..944a702df 100644
--- a/crates/vfs/src/vfs_path.rs
+++ b/crates/vfs/src/vfs_path.rs
@@ -57,23 +57,42 @@ impl VfsPath {
57 }; 57 };
58 buf.push(tag); 58 buf.push(tag);
59 match &self.0 { 59 match &self.0 {
60 VfsPathRepr::PathBuf(it) => { 60 VfsPathRepr::PathBuf(path) => {
61 let path: &std::ffi::OsStr = it.as_os_str();
62 #[cfg(windows)] 61 #[cfg(windows)]
63 { 62 {
64 use std::os::windows::ffi::OsStrExt; 63 use windows_paths::Encode;
65 for wchar in path.encode_wide() { 64 let components = path.components();
66 buf.extend(wchar.to_le_bytes().iter().copied()); 65 let mut add_sep = false;
66 for component in components {
67 if add_sep {
68 windows_paths::SEP.encode(buf);
69 }
70 let len_before = buf.len();
71 match component {
72 std::path::Component::Prefix(prefix) => {
73 // kind() returns a normalized and comparable path prefix.
74 prefix.kind().encode(buf);
75 }
76 std::path::Component::RootDir => {
77 if !add_sep {
78 component.as_os_str().encode(buf);
79 }
80 }
81 _ => component.as_os_str().encode(buf),
82 }
83
84 // some components may be encoded empty
85 add_sep = len_before != buf.len();
67 } 86 }
68 } 87 }
69 #[cfg(unix)] 88 #[cfg(unix)]
70 { 89 {
71 use std::os::unix::ffi::OsStrExt; 90 use std::os::unix::ffi::OsStrExt;
72 buf.extend(path.as_bytes()); 91 buf.extend(path.as_os_str().as_bytes());
73 } 92 }
74 #[cfg(not(any(windows, unix)))] 93 #[cfg(not(any(windows, unix)))]
75 { 94 {
76 buf.extend(path.to_string_lossy().as_bytes()); 95 buf.extend(path.as_os_str().to_string_lossy().as_bytes());
77 } 96 }
78 } 97 }
79 VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()), 98 VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()),
@@ -81,6 +100,112 @@ impl VfsPath {
81 } 100 }
82} 101}
83 102
103#[cfg(windows)]
104mod windows_paths {
105 pub trait Encode {
106 fn encode(&self, buf: &mut Vec<u8>);
107 }
108
109 impl Encode for std::ffi::OsStr {
110 fn encode(&self, buf: &mut Vec<u8>) {
111 use std::os::windows::ffi::OsStrExt;
112 for wchar in self.encode_wide() {
113 buf.extend(wchar.to_le_bytes().iter().copied());
114 }
115 }
116 }
117
118 impl Encode for u8 {
119 fn encode(&self, buf: &mut Vec<u8>) {
120 let wide = *self as u16;
121 buf.extend(wide.to_le_bytes().iter().copied())
122 }
123 }
124
125 impl Encode for &str {
126 fn encode(&self, buf: &mut Vec<u8>) {
127 debug_assert!(self.is_ascii());
128 for b in self.as_bytes() {
129 b.encode(buf)
130 }
131 }
132 }
133
134 pub const SEP: &str = "\\";
135 const VERBATIM: &str = "\\\\?\\";
136 const UNC: &str = "UNC";
137 const DEVICE: &str = "\\\\.\\";
138 const COLON: &str = ":";
139
140 impl Encode for std::path::Prefix<'_> {
141 fn encode(&self, buf: &mut Vec<u8>) {
142 match self {
143 std::path::Prefix::Verbatim(c) => {
144 VERBATIM.encode(buf);
145 c.encode(buf);
146 }
147 std::path::Prefix::VerbatimUNC(server, share) => {
148 VERBATIM.encode(buf);
149 UNC.encode(buf);
150 SEP.encode(buf);
151 server.encode(buf);
152 SEP.encode(buf);
153 share.encode(buf);
154 }
155 std::path::Prefix::VerbatimDisk(d) => {
156 VERBATIM.encode(buf);
157 d.encode(buf);
158 COLON.encode(buf);
159 }
160 std::path::Prefix::DeviceNS(device) => {
161 DEVICE.encode(buf);
162 device.encode(buf);
163 }
164 std::path::Prefix::UNC(server, share) => {
165 SEP.encode(buf);
166 SEP.encode(buf);
167 server.encode(buf);
168 SEP.encode(buf);
169 share.encode(buf);
170 }
171 std::path::Prefix::Disk(d) => {
172 d.encode(buf);
173 COLON.encode(buf);
174 }
175 }
176 }
177 }
178 #[test]
179 fn paths_encoding() {
180 // drive letter casing agnostic
181 test_eq("C:/x.rs", "c:/x.rs");
182 // separator agnostic
183 test_eq("C:/x/y.rs", "C:\\x\\y.rs");
184
185 fn test_eq(a: &str, b: &str) {
186 let mut b1 = Vec::new();
187 let mut b2 = Vec::new();
188 vfs(a).encode(&mut b1);
189 vfs(b).encode(&mut b2);
190 assert_eq!(b1, b2);
191 }
192 }
193
194 #[test]
195 fn test_sep_root_dir_encoding() {
196 let mut buf = Vec::new();
197 vfs("C:/x/y").encode(&mut buf);
198 assert_eq!(&buf, &[0, 67, 0, 58, 0, 92, 0, 120, 0, 92, 0, 121, 0])
199 }
200
201 #[cfg(test)]
202 fn vfs(str: &str) -> super::VfsPath {
203 use super::{AbsPathBuf, VfsPath};
204 use std::convert::TryFrom;
205 VfsPath::from(AbsPathBuf::try_from(str).unwrap())
206 }
207}
208
84#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 209#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
85enum VfsPathRepr { 210enum VfsPathRepr {
86 PathBuf(AbsPathBuf), 211 PathBuf(AbsPathBuf),