use std::future::Future;
use tokio::sync::oneshot;
use crate::client::{Error, SftpClient};
use crate::message::{self, Message, Status, StatusCode};
impl SftpClient {
pub fn request<R: SftpRequest>(
&self,
request: R,
) -> impl Future<Output = Result<R::Reply, Error>> + Send + Sync + 'static {
let sent = if let Some(commands) = &self.commands {
match request.to_request_message() {
Ok(Message::Status(Status {
code: StatusCode::Ok,
..
})) => Err(StatusCode::BadMessage
.to_status("Tried to send an OK status message to the server".into())
.into()),
Ok(Message::Status(status)) => Err(status.into()),
Ok(msg) => {
let (tx, rx) = oneshot::channel();
match commands.send(super::receiver::Request(msg, tx)) {
Ok(()) => Ok(rx),
Err(err) => {
Err(StatusCode::Failure.to_status(err.to_string().into()).into())
}
}
}
Err(err) => Err(err),
}
} else {
Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"SFTP client has been stopped",
)
.into())
};
async move {
match sent?.await {
Ok(msg) => R::from_reply_message(msg?),
Err(_) => Err(std::io::Error::new(
std::io::ErrorKind::ConnectionReset,
"Could not get reply from SFTP client",
)
.into()),
}
}
}
}
pub trait SftpRequest {
type Reply;
fn to_request_message(self) -> Result<Message, Error>;
fn from_reply_message(msg: Message) -> Result<Self::Reply, Error>;
}
impl SftpRequest for Message {
type Reply = Message;
fn to_request_message(self) -> Result<Message, Error> {
Ok(self)
}
fn from_reply_message(msg: Message) -> Result<Self::Reply, Error> {
Ok(msg)
}
}
macro_rules! send_impl {
($input:ident) => {
impl SftpRequest for message::$input {
type Reply = ();
fn to_request_message(self) -> Result<Message, Error> {
Ok(self.into())
}
fn from_reply_message(msg: Message) -> Result<Self::Reply, Error> {
match msg {
Message::Status(status) => status.to_result(()),
_ => Err(StatusCode::BadMessage
.to_status("Expected a status".into())),
}.map_err(Into::into)
}
}
};
($input:ident -> $output:ident) => {
impl SftpRequest for message::$input {
type Reply = message::$output;
fn to_request_message(self) -> Result<Message, Error> {
Ok(self.into())
}
fn from_reply_message(msg: Message) -> Result<Self::Reply, Error> {
match msg {
Message::$output(response) => Ok(response),
Message::Status(status) => Err(status),
_ => Err(StatusCode::BadMessage
.to_status(std::stringify!(Expected a $output or a status).into())),
}.map_err(Into::into)
}
}
};
}
send_impl!(Open -> Handle);
send_impl!(Close);
send_impl!(Read -> Data);
send_impl!(Write);
send_impl!(LStat -> Attrs);
send_impl!(FStat -> Attrs);
send_impl!(SetStat);
send_impl!(FSetStat);
send_impl!(OpenDir -> Handle);
send_impl!(ReadDir -> Name);
send_impl!(Remove);
send_impl!(MkDir);
send_impl!(RmDir);
send_impl!(RealPath -> Name);
send_impl!(Stat -> Attrs);
send_impl!(Rename);
send_impl!(ReadLink -> Name);
send_impl!(Symlink);
send_impl!(Extended -> ExtendedReply);