use bytes::Bytes;
use futures::Future;
use crate::client::{Dir, Error, File, SftpClient, SftpFuture, SftpReply, SftpRequest, StatusCode};
use crate::message::{
Attrs, Close, Data, Extended, ExtendedReply, FSetStat, FStat, Handle, LStat, Message, MkDir,
Name, Open, OpenDir, PFlags, Path, Read, ReadDir, ReadLink, RealPath, Remove, Rename, RmDir,
SetStat, Stat, Status, Symlink, Write,
};
use crate::utils::IntoBytes;
impl SftpClient {
pub fn close(&self, handle: Handle) -> SftpFuture {
self.request(Close { handle })
}
pub fn extended(&self, request: impl IntoBytes, data: impl IntoBytes) -> SftpFuture<Bytes> {
self.request_with(
Extended {
request: request.into_bytes(),
data: data.into_bytes(),
}
.to_request_message(),
(),
|_, msg| Ok(ExtendedReply::from_reply_message(msg)?.data),
)
}
pub fn fsetstat(&self, handle: Handle, attrs: Attrs) -> SftpFuture {
self.request(FSetStat { handle, attrs })
}
pub fn fstat(&self, handle: Handle) -> SftpFuture<Attrs> {
self.request(FStat { handle })
}
pub fn lstat(&self, path: impl Into<Path>) -> SftpFuture<Attrs> {
self.request(LStat { path: path.into() })
}
pub fn mkdir(&self, path: impl Into<Path>) -> SftpFuture {
self.mkdir_with_attrs(path, Attrs::default())
}
pub fn mkdir_with_attrs(&self, path: impl Into<Path>, attrs: Attrs) -> SftpFuture {
self.request(MkDir {
path: path.into(),
attrs,
})
}
pub fn open_handle(
&self,
filename: impl Into<Path>,
pflags: PFlags,
attrs: Attrs,
) -> SftpFuture<Handle> {
self.request(Open {
filename: filename.into(),
pflags,
attrs,
})
}
pub fn open_with_flags_attrs(
&self,
filename: impl Into<Path>,
pflags: PFlags,
attrs: Attrs,
) -> SftpFuture<File, SftpClient> {
self.request_with(
Open {
filename: filename.into(),
pflags,
attrs,
}
.to_request_message(),
self.clone(),
|client, msg| Ok(File::new(client, Handle::from_reply_message(msg)?)),
)
}
pub fn open_with_flags(
&self,
filename: impl Into<Path>,
pflags: PFlags,
) -> SftpFuture<File, SftpClient> {
self.open_with_flags_attrs(filename, pflags, Attrs::default())
}
pub fn open_with_attrs(
&self,
filename: impl Into<Path>,
attrs: Attrs,
) -> SftpFuture<File, SftpClient> {
self.open_with_flags_attrs(filename, PFlags::default(), attrs)
}
pub fn open(&self, filename: impl Into<Path>) -> SftpFuture<File, SftpClient> {
self.open_with_flags_attrs(filename, PFlags::default(), Attrs::default())
}
pub fn opendir_handle(&self, path: impl Into<Path>) -> SftpFuture<Handle> {
self.request(OpenDir { path: path.into() })
}
pub fn opendir(&self, path: impl Into<Path>) -> SftpFuture<Dir, SftpClient> {
self.request_with(
OpenDir { path: path.into() }.to_request_message(),
self.clone(),
|client, msg| Ok(Dir::new(client, Handle::from_reply_message(msg)?)),
)
}
pub fn read(&self, handle: Handle, offset: u64, length: u32) -> SftpFuture<Bytes> {
self.request_with(
Read {
handle,
offset,
length,
}
.to_request_message(),
(),
|_, msg| Ok(Handle::from_reply_message(msg)?.0),
)
}
pub fn readdir_handle(&self, handle: Handle) -> SftpFuture<Name> {
self.request(ReadDir { handle })
}
pub fn readdir(
&self,
path: impl Into<Path>,
) -> impl Future<Output = Result<Name, Error>> + Send + Sync + 'static {
let dir = self.request(OpenDir { path: path.into() });
let client = self.clone();
let mut entries = Name::default();
async move {
let handle = dir.await?;
loop {
match client.readdir_handle(handle.clone()).await {
Ok(mut chunk) => entries.0.append(&mut chunk.0),
Err(Error::Sftp(Status {
code: StatusCode::Eof,
..
})) => break,
Err(err) => {
_ = client.close(handle).await;
return Err(err);
}
}
}
client.close(handle).await?;
Ok(entries)
}
}
pub fn readlink(&self, path: impl Into<Path>) -> SftpFuture<Path> {
self.request_with(
ReadLink { path: path.into() }.to_request_message(),
(),
extract_path_from_name_message,
)
}
pub fn realpath(&self, path: impl Into<Path>) -> SftpFuture<Path> {
self.request_with(
RealPath { path: path.into() }.to_request_message(),
(),
extract_path_from_name_message,
)
}
pub fn remove(&self, path: impl Into<Path>) -> SftpFuture {
self.request(Remove { path: path.into() })
}
pub fn rename(&self, old_path: impl Into<Path>, new_path: impl Into<Path>) -> SftpFuture {
self.request(Rename {
old_path: old_path.into(),
new_path: new_path.into(),
})
}
pub fn rmdir(&self, path: impl Into<Path>) -> SftpFuture {
self.request(RmDir { path: path.into() })
}
pub fn setstat(&self, path: impl Into<Path>, attrs: Attrs) -> SftpFuture {
self.request(SetStat {
path: path.into(),
attrs,
})
}
pub fn stat(&self, path: impl Into<Path>) -> SftpFuture<Attrs> {
self.request(Stat { path: path.into() })
}
pub fn symlink(&self, link_path: impl Into<Path>, target_path: impl Into<Path>) -> SftpFuture {
self.request(Symlink {
link_path: link_path.into(),
target_path: target_path.into(),
})
}
pub fn write(&self, handle: Handle, offset: u64, data: impl Into<Data>) -> SftpFuture {
self.request(Write {
handle,
offset,
data: data.into(),
})
}
}
fn extract_path_from_name_message(_: (), msg: Message) -> Result<Path, Error> {
match Name::from_reply_message(msg)?.as_mut() {
[] => Err(Error::Sftp(StatusCode::BadMessage.to_status("No entry"))),
[entry] => Ok(std::mem::take(entry).filename),
_ => Err(Error::Sftp(
StatusCode::BadMessage.to_status("Multiple entries"),
)),
}
}