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 + Send, data: impl Into> + 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 + Send, text: impl Into + 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 + Send, dst: impl Into + 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 + 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) -> ClientResult { 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; /// Retrieves metadata about a path on a remote machine. async fn metadata( &mut self, path: impl Into, canonicalize: bool, resolve_file_type: bool, ) -> io::Result; /// Sets permissions for a path on a remote machine. async fn set_permissions( &mut self, path: impl Into, permissions: protocol::Permissions, options: protocol::SetPermissionsOptions, ) -> io::Result<()>; /// Perform a search. async fn search( &mut self, query: impl Into, ) -> io::Result; /// 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, depth: usize, absolute: bool, canonicalize: bool, include_root: bool, ) -> io::Result<(Vec, Vec)>; /// Reads a remote file as a collection of bytes. async fn read_file(&mut self, path: impl Into) -> io::Result>; /// Returns a remote file as a string. async fn read_file_text(&mut self, path: impl Into) -> io::Result; /// Removes a remote file or directory, supporting removal of non-empty directories if /// force is true. async fn remove(&mut self, path: impl Into, force: bool) -> io::Result<()>; /// Renames a remote file or directory from src to dst. async fn rename(&mut self, src: impl Into, dst: impl Into) -> io::Result<()>; /// Watches a remote file or directory. async fn watch( &mut self, path: impl Into, recursive: bool, only: impl Into, except: impl Into, ) -> io::Result; /// Unwatches a remote file or directory. async fn unwatch(&mut self, path: impl Into) -> io::Result<()>; /// Spawns a process on the remote machine. async fn spawn( &mut self, cmd: impl Into, environment: Environment, current_dir: Option, pty: Option, ) -> io::Result; /// Spawns an LSP process on the remote machine. async fn spawn_lsp( &mut self, cmd: impl Into, environment: Environment, current_dir: Option, pty: Option, ) -> io::Result; /// Spawns a process on the remote machine and wait for it to complete. async fn output( &mut self, cmd: impl Into, environment: Environment, current_dir: Option, pty: Option, ) -> io::Result; /// Retrieves information about the remote system. async fn system_info(&mut self) -> io::Result; /// Retrieves server version information. async fn version(&mut self) -> io::Result; /// 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, data: impl Into>, ) -> io::Result<()>; /// Writes a remote file with the data from a string. async fn write_file_text( &mut self, path: impl Into, data: impl Into, ) -> io::Result<()>; } impl ClientExt for T {}