More progress....

feat/RusshSupport
Chip Senkbeil 6 months ago
parent 8e1506f6de
commit 0690de67c5
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

30
Cargo.lock generated

@ -842,7 +842,7 @@ dependencies = [
"serde", "serde",
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"strum", "strum 0.24.1",
"test-log", "test-log",
"tokio", "tokio",
] ]
@ -914,7 +914,7 @@ dependencies = [
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"sha2 0.10.6", "sha2 0.10.6",
"strum", "strum 0.24.1",
"tempfile", "tempfile",
"test-log", "test-log",
"tokio", "tokio",
@ -934,7 +934,7 @@ dependencies = [
"serde", "serde",
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"strum", "strum 0.25.0",
] ]
[[package]] [[package]]
@ -3221,7 +3221,16 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [ dependencies = [
"strum_macros", "strum_macros 0.24.3",
]
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros 0.25.3",
] ]
[[package]] [[package]]
@ -3237,6 +3246,19 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.36",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.4.1" version = "2.4.1"

@ -23,7 +23,7 @@ regex = "1.8.3"
semver = { version = "1.0.17", features = ["serde"] } semver = { version = "1.0.17", features = ["serde"] }
serde = { version = "1.0.163", features = ["derive"] } serde = { version = "1.0.163", features = ["derive"] }
serde_bytes = "0.11.9" serde_bytes = "0.11.9"
strum = { version = "0.24.1", features = ["derive"] } strum = { version = "0.25.0", features = ["derive"] }
[dev-dependencies] [dev-dependencies]
rmp = "0.8.11" rmp = "0.8.11"

@ -2,7 +2,7 @@ use std::io;
use derive_more::IsVariant; use derive_more::IsVariant;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::{AsRefStr, EnumDiscriminants, EnumIter, EnumMessage, EnumString}; use strum::{AsRefStr, EnumDiscriminants, EnumIter, EnumMessage, EnumString, IntoStaticStr};
use crate::common::{ use crate::common::{
Change, DirEntry, Error, Metadata, ProcessId, SearchId, SearchQueryMatch, SystemInfo, Version, Change, DirEntry, Error, Metadata, ProcessId, SearchId, SearchQueryMatch, SystemInfo, Version,
@ -10,7 +10,16 @@ use crate::common::{
/// Represents the payload of a successful response /// Represents the payload of a successful response
#[derive( #[derive(
Clone, Debug, PartialEq, Eq, AsRefStr, IsVariant, EnumDiscriminants, Serialize, Deserialize, Clone,
Debug,
PartialEq,
Eq,
AsRefStr,
IsVariant,
EnumDiscriminants,
Serialize,
Deserialize,
IntoStaticStr,
)] )]
#[strum_discriminants(derive( #[strum_discriminants(derive(
AsRefStr, AsRefStr,
@ -21,6 +30,7 @@ use crate::common::{
Hash, Hash,
PartialOrd, PartialOrd,
Ord, Ord,
IntoStaticStr,
IsVariant, IsVariant,
Serialize, Serialize,
Deserialize Deserialize

@ -1,7 +1,8 @@
use std::io; use std::io;
use async_trait::async_trait; use async_trait::async_trait;
use distant_core_protocol::Response;
use crate::protocol;
/// Represents a context associated when an API request is being executed, supporting the ability /// Represents a context associated when an API request is being executed, supporting the ability
/// to send responses back asynchronously. /// to send responses back asynchronously.
@ -14,5 +15,5 @@ pub trait Ctx: Send {
fn clone_ctx(&self) -> Box<dyn Ctx>; fn clone_ctx(&self) -> Box<dyn Ctx>;
/// Sends some response back. /// Sends some response back.
fn send(&self, response: Response) -> io::Result<()>; fn send(&self, msg: protocol::Msg<protocol::Response>) -> io::Result<()>;
} }

@ -2,13 +2,19 @@ use std::io;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use distant_core_protocol::{Error, Request, Response};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::api::{ use crate::api::{
Api, Ctx, FileSystemApi, ProcessApi, SearchApi, SystemInfoApi, VersionApi, WatchApi, Api, Ctx, FileSystemApi, ProcessApi, SearchApi, SystemInfoApi, VersionApi, WatchApi,
}; };
use crate::common::Stream; use crate::common::{Request, Response, Stream};
use crate::protocol;
mod err;
mod ext;
pub use err::{ClientError, ClientResult};
pub use ext::ClientExt;
/// Full API for a distant-compatible client. /// Full API for a distant-compatible client.
#[async_trait] #[async_trait]
@ -66,9 +72,9 @@ impl<T: Api + 'static> Client for ClientBridge<T> {
Box::new(__Ctx(self.0, self.1.clone())) Box::new(__Ctx(self.0, self.1.clone()))
} }
fn send(&self, response: Response) -> io::Result<()> { fn send(&self, msg: protocol::Msg<protocol::Response>) -> io::Result<()> {
self.1 self.1
.send(response) .send(msg)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Bridge has closed")) .map_err(|_| io::Error::new(io::ErrorKind::Other, "Bridge has closed"))
} }
} }
@ -95,62 +101,102 @@ impl<T: Api + 'static> Client for ClientBridge<T> {
} }
} }
/// Processes an incoming request.
async fn handle_request<T>(api: Arc<T>, ctx: Box<dyn Ctx>, request: Request) -> Response async fn handle_request<T>(api: Arc<T>, ctx: Box<dyn Ctx>, request: Request) -> Response
where
T: Api,
{
let origin = request.id;
let sequence = request.flags.sequence;
Response {
id: rand::random(),
origin,
payload: match request.payload {
protocol::Msg::Single(request) => {
protocol::Msg::Single(handle_protocol_request(api, ctx, request).await)
}
protocol::Msg::Batch(requests) if sequence => {
let mut responses = Vec::new();
for request in requests {
responses.push(
handle_protocol_request(Arc::clone(&api), ctx.clone_ctx(), request).await,
);
}
protocol::Msg::Batch(responses)
}
protocol::Msg::Batch(requests) => {
let mut responses = Vec::new();
for request in requests {
responses.push(
handle_protocol_request(Arc::clone(&api), ctx.clone_ctx(), request).await,
);
}
protocol::Msg::Batch(responses)
}
},
}
}
/// Processes a singular protocol request using the provided api and ctx.
async fn handle_protocol_request<T>(
api: Arc<T>,
ctx: Box<dyn Ctx>,
request: protocol::Request,
) -> protocol::Response
where where
T: Api, T: Api,
{ {
match request { match request {
Request::Version {} => { protocol::Request::Version {} => {
let api = api.version(); let api = api.version();
api.version(ctx) api.version(ctx)
.await .await
.map(Response::Version) .map(protocol::Response::Version)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::FileRead { path } => { protocol::Request::FileRead { path } => {
let api = api.file_system(); let api = api.file_system();
api.read_file(ctx, path) api.read_file(ctx, path)
.await .await
.map(|data| Response::Blob { data }) .map(|data| protocol::Response::Blob { data })
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::FileReadText { path } => { protocol::Request::FileReadText { path } => {
let api = api.file_system(); let api = api.file_system();
api.read_file_text(ctx, path) api.read_file_text(ctx, path)
.await .await
.map(|data| Response::Text { data }) .map(|data| protocol::Response::Text { data })
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::FileWrite { path, data } => { protocol::Request::FileWrite { path, data } => {
let api = api.file_system(); let api = api.file_system();
api.write_file(ctx, path, data) api.write_file(ctx, path, data)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::FileWriteText { path, text } => { protocol::Request::FileWriteText { path, text } => {
let api = api.file_system(); let api = api.file_system();
api.write_file_text(ctx, path, text) api.write_file_text(ctx, path, text)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::FileAppend { path, data } => { protocol::Request::FileAppend { path, data } => {
let api = api.file_system(); let api = api.file_system();
api.append_file(ctx, path, data) api.append_file(ctx, path, data)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::FileAppendText { path, text } => { protocol::Request::FileAppendText { path, text } => {
let api = api.file_system(); let api = api.file_system();
api.append_file_text(ctx, path, text) api.append_file_text(ctx, path, text)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::DirRead { protocol::Request::DirRead {
path, path,
depth, depth,
absolute, absolute,
@ -160,41 +206,41 @@ where
let api = api.file_system(); let api = api.file_system();
api.read_dir(ctx, path, depth, absolute, canonicalize, include_root) api.read_dir(ctx, path, depth, absolute, canonicalize, include_root)
.await .await
.map(|(entries, errors)| Response::DirEntries { .map(|(entries, errors)| protocol::Response::DirEntries {
entries, entries,
errors: errors.into_iter().map(Error::from).collect(), errors: errors.into_iter().map(protocol::Error::from).collect(),
}) })
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::DirCreate { path, all } => { protocol::Request::DirCreate { path, all } => {
let api = api.file_system(); let api = api.file_system();
api.create_dir(ctx, path, all) api.create_dir(ctx, path, all)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Remove { path, force } => { protocol::Request::Remove { path, force } => {
let api = api.file_system(); let api = api.file_system();
api.remove(ctx, path, force) api.remove(ctx, path, force)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Copy { src, dst } => { protocol::Request::Copy { src, dst } => {
let api = api.file_system(); let api = api.file_system();
api.copy(ctx, src, dst) api.copy(ctx, src, dst)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Rename { src, dst } => { protocol::Request::Rename { src, dst } => {
let api = api.file_system(); let api = api.file_system();
api.rename(ctx, src, dst) api.rename(ctx, src, dst)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Watch { protocol::Request::Watch {
path, path,
recursive, recursive,
only, only,
@ -203,24 +249,24 @@ where
let api = api.watch(); let api = api.watch();
api.watch(ctx, path, recursive, only, except) api.watch(ctx, path, recursive, only, except)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Unwatch { path } => { protocol::Request::Unwatch { path } => {
let api = api.watch(); let api = api.watch();
api.unwatch(ctx, path) api.unwatch(ctx, path)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Exists { path } => { protocol::Request::Exists { path } => {
let api = api.file_system(); let api = api.file_system();
api.exists(ctx, path) api.exists(ctx, path)
.await .await
.map(|value| Response::Exists { value }) .map(|value| protocol::Response::Exists { value })
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Metadata { protocol::Request::Metadata {
path, path,
canonicalize, canonicalize,
resolve_file_type, resolve_file_type,
@ -228,10 +274,10 @@ where
let api = api.file_system(); let api = api.file_system();
api.metadata(ctx, path, canonicalize, resolve_file_type) api.metadata(ctx, path, canonicalize, resolve_file_type)
.await .await
.map(Response::Metadata) .map(protocol::Response::Metadata)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::SetPermissions { protocol::Request::SetPermissions {
path, path,
permissions, permissions,
options, options,
@ -239,24 +285,24 @@ where
let api = api.file_system(); let api = api.file_system();
api.set_permissions(ctx, path, permissions, options) api.set_permissions(ctx, path, permissions, options)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::Search { query } => { protocol::Request::Search { query } => {
let api = api.search(); let api = api.search();
api.search(ctx, query) api.search(ctx, query)
.await .await
.map(|id| Response::SearchStarted { id }) .map(|id| protocol::Response::SearchStarted { id })
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::CancelSearch { id } => { protocol::Request::CancelSearch { id } => {
let api = api.search(); let api = api.search();
api.cancel_search(ctx, id) api.cancel_search(ctx, id)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::ProcSpawn { protocol::Request::ProcSpawn {
cmd, cmd,
environment, environment,
current_dir, current_dir,
@ -265,36 +311,36 @@ where
let api = api.process(); let api = api.process();
api.proc_spawn(ctx, cmd.into(), environment, current_dir, pty) api.proc_spawn(ctx, cmd.into(), environment, current_dir, pty)
.await .await
.map(|id| Response::ProcSpawned { id }) .map(|id| protocol::Response::ProcSpawned { id })
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::ProcKill { id } => { protocol::Request::ProcKill { id } => {
let api = api.process(); let api = api.process();
api.proc_kill(ctx, id) api.proc_kill(ctx, id)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::ProcStdin { id, data } => { protocol::Request::ProcStdin { id, data } => {
let api = api.process(); let api = api.process();
api.proc_stdin(ctx, id, data) api.proc_stdin(ctx, id, data)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::ProcResizePty { id, size } => { protocol::Request::ProcResizePty { id, size } => {
let api = api.process(); let api = api.process();
api.proc_resize_pty(ctx, id, size) api.proc_resize_pty(ctx, id, size)
.await .await
.map(|_| Response::Ok) .map(|_| protocol::Response::Ok)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
Request::SystemInfo {} => { protocol::Request::SystemInfo {} => {
let api = api.system_info(); let api = api.system_info();
api.system_info(ctx) api.system_info(ctx)
.await .await
.map(Response::SystemInfo) .map(protocol::Response::SystemInfo)
.unwrap_or_else(Response::from) .unwrap_or_else(protocol::Response::from)
} }
} }
} }

@ -0,0 +1,75 @@
use std::error;
use std::fmt;
use std::io;
use crate::common::Id;
use crate::protocol;
pub type ClientResult<T> = Result<T, ClientError>;
/// Errors that can occur from sending data using a client.
#[derive(Debug)]
pub enum ClientError {
/// A networking error occurred when trying to submit the request.
Io(io::Error),
/// An error occurred server-side.
Server(protocol::Error),
/// A response was received, but its origin did not match the request.
WrongOrigin { expected: Id, actual: Id },
/// A response was received, but the payload was single when expected batch or vice versa.
WrongPayloadFormat,
/// A response was received, but its payload type did not match any expected response type.
WrongPayloadType {
expected: &'static [&'static str],
actual: &'static str,
},
}
impl error::Error for ClientError {}
impl fmt::Display for ClientError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(x) => fmt::Display::fmt(x, f),
Self::Server(x) => fmt::Display::fmt(x, f),
Self::WrongOrigin { expected, actual } => {
write!(
f,
"Wrong response origin! Expected {expected}, got {actual}."
)
}
Self::WrongPayloadFormat => write!(f, "Wrong response payload format!"),
Self::WrongPayloadType { expected, actual } => {
if expected.len() == 1 {
let expected = expected[0];
write!(
f,
"Wrong response payload type! Wanted {expected}, but got {actual}."
)
} else {
let expected = expected.join(",");
write!(
f,
"Wrong response type! Wanted one of {expected}, but got {actual}."
)
}
}
}
}
}
impl From<io::Error> for ClientError {
fn from(x: io::Error) -> Self {
Self::Io(x)
}
}
impl From<protocol::Error> for ClientError {
fn from(x: protocol::Error) -> Self {
Self::Server(x)
}
}

@ -0,0 +1,228 @@
use async_trait::async_trait;
use std::io;
use std::path::PathBuf;
use super::{Client, ClientError, ClientResult};
use crate::common::{Request, RequestFlags, Response};
use crate::protocol;
macro_rules! impl_client_fn {
($this:ident, $res:expr, $payload:expr) => {{
let id = rand::random();
let request = Request {
id,
flags: RequestFlags {
sequence: false,
..Default::default()
},
payload: protocol::Msg::Single($payload),
};
let response = $this.ask(request).await?;
if response.origin != id {
return Err(ClientError::WrongOrigin {
expected: id,
actual: response.origin,
});
}
match response.payload {
protocol::Msg::Single(protocol::Response::Error(x)) => Err(ClientError::Server(x)),
protocol::Msg::Single(x) if protocol::ResponseKind::from(&x) == $res => Ok(()),
protocol::Msg::Single(x) => Err(ClientError::WrongPayloadType {
expected: &[$res.into()],
actual: x.into(),
}),
protocol::Msg::Batch(_) => Err(ClientError::WrongPayloadFormat),
}
}};
}
/// Provides convenience functions on top of a [`Client`].
#[async_trait]
pub trait ClientExt: Client {
/// Appends to a remote file using the data from a collection of bytes.
async fn append_file(
&mut self,
path: impl Into<PathBuf> + Send,
data: impl Into<Vec<u8>> + Send,
) -> ClientResult<()> {
impl_client_fn!(
self,
protocol::ResponseKind::Ok,
protocol::Request::FileAppend {
path: path.into(),
data: data.into(),
}
)
}
/// Appends to a remote file using the data from a string.
async fn append_file_text(
&mut self,
path: impl Into<PathBuf> + Send,
text: impl Into<String> + Send,
) -> ClientResult<()> {
impl_client_fn!(
self,
protocol::ResponseKind::Ok,
protocol::Request::FileAppendText {
path: path.into(),
text: text.into(),
}
)
}
/// Copies a remote file or directory from src to dst.
async fn copy(
&mut self,
src: impl Into<PathBuf> + Send,
dst: impl Into<PathBuf> + Send,
) -> ClientResult<()> {
impl_client_fn!(
self,
protocol::ResponseKind::Ok,
protocol::Request::Copy {
src: src.into(),
dst: dst.into(),
}
)
}
/// Creates a remote directory, optionally creating all parent components if specified.
async fn create_dir(&mut self, path: impl Into<PathBuf> + Send, all: bool) -> ClientResult<()> {
impl_client_fn!(
self,
protocol::ResponseKind::Ok,
protocol::Request::DirCreate {
path: path.into(),
all,
}
)
}
/// Checks whether the `path` exists on the remote machine.
async fn exists(&mut self, path: impl Into<PathBuf>) -> ClientResult<bool> {
impl_client_fn!(
self,
protocol::ResponseKind::Exists,
protocol::Request::Exists { path: path.into() }
)
}
/// Checks whether this client is compatible with the remote server.
async fn is_compatible(&mut self) -> io::Result<bool>;
/// Retrieves metadata about a path on a remote machine.
async fn metadata(
&mut self,
path: impl Into<PathBuf>,
canonicalize: bool,
resolve_file_type: bool,
) -> io::Result<protocol::Metadata>;
/// Sets permissions for a path on a remote machine.
async fn set_permissions(
&mut self,
path: impl Into<PathBuf>,
permissions: protocol::Permissions,
options: protocol::SetPermissionsOptions,
) -> io::Result<()>;
/// Perform a search.
async fn search(
&mut self,
query: impl Into<protocol::SearchQuery>,
) -> io::Result<protocol::Searcher>;
/// Cancel an active search query.
async fn cancel_search(&mut self, id: protocol::SearchId) -> io::Result<()>;
/// Reads entries from a directory, returning a tuple of directory entries and errors.
async fn read_dir(
&mut self,
path: impl Into<PathBuf>,
depth: usize,
absolute: bool,
canonicalize: bool,
include_root: bool,
) -> io::Result<(Vec<protocol::DirEntry>, Vec<protocol::Error>)>;
/// Reads a remote file as a collection of bytes.
async fn read_file(&mut self, path: impl Into<PathBuf>) -> io::Result<Vec<u8>>;
/// Returns a remote file as a string.
async fn read_file_text(&mut self, path: impl Into<PathBuf>) -> io::Result<String>;
/// Removes a remote file or directory, supporting removal of non-empty directories if
/// force is true.
async fn remove(&mut self, path: impl Into<PathBuf>, force: bool) -> io::Result<()>;
/// Renames a remote file or directory from src to dst.
async fn rename(&mut self, src: impl Into<PathBuf>, dst: impl Into<PathBuf>) -> io::Result<()>;
/// Watches a remote file or directory.
async fn watch(
&mut self,
path: impl Into<PathBuf>,
recursive: bool,
only: impl Into<protocol::ChangeKindSet>,
except: impl Into<protocol::ChangeKindSet>,
) -> io::Result<Watcher>;
/// Unwatches a remote file or directory.
async fn unwatch(&mut self, path: impl Into<PathBuf>) -> io::Result<()>;
/// Spawns a process on the remote machine.
async fn spawn(
&mut self,
cmd: impl Into<String>,
environment: Environment,
current_dir: Option<PathBuf>,
pty: Option<protocol::PtySize>,
) -> io::Result<RemoteProcess>;
/// Spawns an LSP process on the remote machine.
async fn spawn_lsp(
&mut self,
cmd: impl Into<String>,
environment: Environment,
current_dir: Option<PathBuf>,
pty: Option<protocol::PtySize>,
) -> io::Result<RemoteLspProcess>;
/// Spawns a process on the remote machine and wait for it to complete.
async fn output(
&mut self,
cmd: impl Into<String>,
environment: Environment,
current_dir: Option<PathBuf>,
pty: Option<protocol::PtySize>,
) -> io::Result<RemoteOutput>;
/// Retrieves information about the remote system.
async fn system_info(&mut self) -> io::Result<protocol::SystemInfo>;
/// Retrieves server version information.
async fn version(&mut self) -> io::Result<protocol::Version>;
/// Returns version of protocol that the client uses.
async fn protocol_version(&self) -> protocol::semver::Version;
/// Writes a remote file with the data from a collection of bytes.
async fn write_file(
&mut self,
path: impl Into<PathBuf>,
data: impl Into<Vec<u8>>,
) -> io::Result<()>;
/// Writes a remote file with the data from a string.
async fn write_file_text(
&mut self,
path: impl Into<PathBuf>,
data: impl Into<String>,
) -> io::Result<()>;
}
impl<T: Client + ?Sized> ClientExt for T {}

@ -1,8 +1,10 @@
mod destination; mod destination;
mod map; mod map;
mod msg;
mod stream; mod stream;
mod utils; mod utils;
pub use destination::{Destination, Host, HostParseError}; pub use destination::{Destination, Host, HostParseError};
pub use map::{Map, MapParseError}; pub use map::{Map, MapParseError};
pub use msg::{Id, Request, RequestFlags, Response};
pub use stream::Stream; pub use stream::Stream;

@ -0,0 +1,37 @@
use serde::{Deserialize, Serialize};
use crate::protocol;
/// Represents an id associated with a request or response.
pub type Id = u64;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Request {
pub id: Id,
pub flags: RequestFlags,
pub payload: protocol::Msg<protocol::Request>,
}
impl From<protocol::Msg<protocol::Request>> for Request {
fn from(msg: protocol::Msg<protocol::Request>) -> Self {
Self {
id: rand::random(),
flags: Default::default(),
payload: msg,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct RequestFlags {
/// If true, payload should be executed in sequence; otherwise,
/// a batch payload can be executed in any order.
pub sequence: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Response {
pub id: Id,
pub origin: Id,
pub payload: protocol::Msg<protocol::Response>,
}

@ -112,7 +112,8 @@ impl PluginRegistry {
impl std::fmt::Debug for PluginRegistry { impl std::fmt::Debug for PluginRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PluginHandlerRegistry") f.debug_struct("PluginRegistry")
.field("loaded", &self.loaded)
.field("launch_handlers", &self.launch_handlers.keys()) .field("launch_handlers", &self.launch_handlers.keys())
.field("connect_handlers", &self.connect_handlers.keys()) .field("connect_handlers", &self.connect_handlers.keys())
.finish() .finish()

Loading…
Cancel
Save