use std::{
future::Future,
pin::Pin,
task::{ready, Poll},
};
use crate::client::{Error, SftpClientStopping, SftpFuture};
use crate::message::Handle;
use super::Dir;
impl Dir {
pub fn is_closed(&self) -> bool {
self.handle.is_none()
}
pub fn close(&mut self) -> impl Future<Output = Result<(), Error>> + Drop + Send + Sync + '_ {
DirClosing::new(self)
}
}
impl Drop for Dir {
fn drop(&mut self) {
DirClosing::new(self).forget()
}
}
struct DirClosing<'a>(DirClosingState<'a>);
enum DirClosingState<'a> {
Closing {
dir: &'a mut Dir,
handle: Handle,
pending: SftpFuture,
},
Stopping(SftpClientStopping<'a>),
Closed,
}
impl<'a> DirClosing<'a> {
fn new(dir: &'a mut Dir) -> Self {
dir.buffer = None;
dir.pending = None;
if let Some(handle) = dir.handle.take() {
log::trace!("wait for closing");
let pending = dir.client.close(handle.clone());
return DirClosing(DirClosingState::Closing {
dir,
handle,
pending,
});
};
let stop = SftpClientStopping::new(&mut dir.client);
if stop.is_stopped() {
log::trace!("closed and stopped");
DirClosing(DirClosingState::Closed)
} else {
log::trace!("closed, wait for stopping");
DirClosing(DirClosingState::Stopping(stop))
}
}
fn forget(mut self) {
match std::mem::replace(&mut self.0, DirClosingState::Closed) {
DirClosingState::Closing {
dir,
handle: _,
pending: _,
} => {
log::trace!("Directory dropped while not closed");
SftpClientStopping::new(&mut dir.client).forget()
}
DirClosingState::Stopping(stopping) => stopping.forget(),
DirClosingState::Closed => (),
}
}
}
impl Drop for DirClosing<'_> {
fn drop(&mut self) {
match &mut self.0 {
DirClosingState::Closing { dir, handle, .. } => {
dir.handle = Some(std::mem::take(handle))
}
DirClosingState::Stopping { .. } => (),
DirClosingState::Closed => (),
}
}
}
impl Future for DirClosing<'_> {
type Output = Result<(), Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
loop {
match &mut self.0 {
DirClosingState::Closing { pending, .. } => {
if let Err(err) = ready!(Pin::new(pending).poll(cx)) {
return Poll::Ready(Err(err));
}
let DirClosingState::Closing { dir, .. } =
std::mem::replace(&mut self.0, DirClosingState::Closed)
else {
unreachable!()
};
self.0 = DirClosingState::Stopping(SftpClientStopping::new(&mut dir.client));
}
DirClosingState::Stopping(stop) => {
ready!(Pin::new(stop).poll(cx));
self.0 = DirClosingState::Closed;
return Poll::Ready(Ok(()));
}
DirClosingState::Closed => return Poll::Ready(Ok(())),
}
}
}
}