windows: Add support for SSH (#29145)
Closes #19892 This PR builds on top of #20587 and improves upon it. Release Notes: - N/A --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
parent
8bd739d869
commit
0ca0914cca
26 changed files with 1435 additions and 354 deletions
|
@ -27,3 +27,4 @@ prost-build.workspace = true
|
|||
|
||||
[dev-dependencies]
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
typed-path = "0.11"
|
||||
|
|
|
@ -127,51 +127,46 @@ pub trait ToProto {
|
|||
fn to_proto(self) -> String;
|
||||
}
|
||||
|
||||
impl FromProto for PathBuf {
|
||||
#[inline]
|
||||
fn from_proto_path(proto: String) -> PathBuf {
|
||||
#[cfg(target_os = "windows")]
|
||||
fn from_proto(proto: String) -> Self {
|
||||
proto.split("/").collect()
|
||||
}
|
||||
let proto = proto.replace('/', "\\");
|
||||
|
||||
PathBuf::from(proto)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_proto_path(path: &Path) -> String {
|
||||
#[cfg(target_os = "windows")]
|
||||
let proto = path.to_string_lossy().replace('\\', "/");
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let proto = path.to_string_lossy().to_string();
|
||||
|
||||
proto
|
||||
}
|
||||
|
||||
impl FromProto for PathBuf {
|
||||
fn from_proto(proto: String) -> Self {
|
||||
PathBuf::from(proto)
|
||||
from_proto_path(proto)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromProto for Arc<Path> {
|
||||
fn from_proto(proto: String) -> Self {
|
||||
PathBuf::from_proto(proto).into()
|
||||
from_proto_path(proto).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToProto for PathBuf {
|
||||
#[cfg(target_os = "windows")]
|
||||
fn to_proto(self) -> String {
|
||||
self.components()
|
||||
.map(|comp| comp.as_os_str().to_string_lossy().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("/")
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn to_proto(self) -> String {
|
||||
self.to_string_lossy().to_string()
|
||||
to_proto_path(&self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToProto for &Path {
|
||||
#[cfg(target_os = "windows")]
|
||||
fn to_proto(self) -> String {
|
||||
self.components()
|
||||
.map(|comp| comp.as_os_str().to_string_lossy().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("/")
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn to_proto(self) -> String {
|
||||
self.to_string_lossy().to_string()
|
||||
to_proto_path(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,3 +209,103 @@ impl<T: RequestMessage> TypedEnvelope<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use typed_path::{UnixPath, UnixPathBuf, WindowsPath, WindowsPathBuf};
|
||||
|
||||
fn windows_path_from_proto(proto: String) -> WindowsPathBuf {
|
||||
let proto = proto.replace('/', "\\");
|
||||
WindowsPathBuf::from(proto)
|
||||
}
|
||||
|
||||
fn unix_path_from_proto(proto: String) -> UnixPathBuf {
|
||||
UnixPathBuf::from(proto)
|
||||
}
|
||||
|
||||
fn windows_path_to_proto(path: &WindowsPath) -> String {
|
||||
path.to_string_lossy().replace('\\', "/")
|
||||
}
|
||||
|
||||
fn unix_path_to_proto(path: &UnixPath) -> String {
|
||||
path.to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_proto_interop() {
|
||||
const WINDOWS_PATHS: &[&str] = &[
|
||||
"C:\\Users\\User\\Documents\\file.txt",
|
||||
"C:/Program Files/App/app.exe",
|
||||
"projects\\zed\\crates\\proto\\src\\typed_envelope.rs",
|
||||
"projects/my project/src/main.rs",
|
||||
];
|
||||
const UNIX_PATHS: &[&str] = &[
|
||||
"/home/user/documents/file.txt",
|
||||
"/usr/local/bin/my app/app",
|
||||
"projects/zed/crates/proto/src/typed_envelope.rs",
|
||||
"projects/my project/src/main.rs",
|
||||
];
|
||||
|
||||
// Windows path to proto and back
|
||||
for &windows_path_str in WINDOWS_PATHS {
|
||||
let windows_path = WindowsPathBuf::from(windows_path_str);
|
||||
let proto = windows_path_to_proto(&windows_path);
|
||||
let recovered_path = windows_path_from_proto(proto);
|
||||
assert_eq!(windows_path, recovered_path);
|
||||
assert_eq!(
|
||||
recovered_path.to_string_lossy(),
|
||||
windows_path_str.replace('/', "\\")
|
||||
);
|
||||
}
|
||||
// Unix path to proto and back
|
||||
for &unix_path_str in UNIX_PATHS {
|
||||
let unix_path = UnixPathBuf::from(unix_path_str);
|
||||
let proto = unix_path_to_proto(&unix_path);
|
||||
let recovered_path = unix_path_from_proto(proto);
|
||||
assert_eq!(unix_path, recovered_path);
|
||||
assert_eq!(recovered_path.to_string_lossy(), unix_path_str);
|
||||
}
|
||||
// Windows host, Unix client, host sends Windows path to client
|
||||
for &windows_path_str in WINDOWS_PATHS {
|
||||
let windows_host_path = WindowsPathBuf::from(windows_path_str);
|
||||
let proto = windows_path_to_proto(&windows_host_path);
|
||||
let unix_client_received_path = unix_path_from_proto(proto);
|
||||
let proto = unix_path_to_proto(&unix_client_received_path);
|
||||
let windows_host_recovered_path = windows_path_from_proto(proto);
|
||||
assert_eq!(windows_host_path, windows_host_recovered_path);
|
||||
assert_eq!(
|
||||
windows_host_recovered_path.to_string_lossy(),
|
||||
windows_path_str.replace('/', "\\")
|
||||
);
|
||||
}
|
||||
// Unix host, Windows client, host sends Unix path to client
|
||||
for &unix_path_str in UNIX_PATHS {
|
||||
let unix_host_path = UnixPathBuf::from(unix_path_str);
|
||||
let proto = unix_path_to_proto(&unix_host_path);
|
||||
let windows_client_received_path = windows_path_from_proto(proto);
|
||||
let proto = windows_path_to_proto(&windows_client_received_path);
|
||||
let unix_host_recovered_path = unix_path_from_proto(proto);
|
||||
assert_eq!(unix_host_path, unix_host_recovered_path);
|
||||
assert_eq!(unix_host_recovered_path.to_string_lossy(), unix_path_str);
|
||||
}
|
||||
}
|
||||
|
||||
// todo(zjk)
|
||||
#[test]
|
||||
fn test_unsolved_case() {
|
||||
// Unix host, Windows client
|
||||
// The Windows client receives a Unix path with backslashes in it, then
|
||||
// sends it back to the host.
|
||||
// This currently fails.
|
||||
let unix_path = UnixPathBuf::from("/home/user/projects/my\\project/src/main.rs");
|
||||
let proto = unix_path_to_proto(&unix_path);
|
||||
let windows_client_received_path = windows_path_from_proto(proto);
|
||||
let proto = windows_path_to_proto(&windows_client_received_path);
|
||||
let unix_host_recovered_path = unix_path_from_proto(proto);
|
||||
assert_ne!(unix_path, unix_host_recovered_path);
|
||||
assert_eq!(
|
||||
unix_host_recovered_path.to_string_lossy(),
|
||||
"/home/user/projects/my/project/src/main.rs"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue