aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-08-14 11:06:03 +0100
committerGitHub <[email protected]>2020-08-14 11:06:03 +0100
commit674af600f105abe1dcb6c7654058e7df5a402429 (patch)
treeb1c9f3e698780720aa2c92dbfb9c2e3ae07a4bdd /crates
parentf1f73649a686dc6e6449afc35e0fa6fed00e225d (diff)
parent8fc254597f7351e06b19052933aa01a044583b71 (diff)
Merge #5756
5756: Sophisticate Windows path encoding r=matklad a=pragmatrix As discussed in #5475, path encoding should be agnostic of the drive letter casing on Windows. Compared to the problem it solves, the code added seems a lot and may introduce other problems. But I've not found a simpler way basing this on the public API surface that Rust offers. Fixes #5484. cc @Emilgardis Co-authored-by: Armin Sander <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/vfs/src/vfs_path.rs139
1 files changed, 132 insertions, 7 deletions
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),