use std::{
future::Future,
pin::Pin,
sync::Arc,
task::{ready, Poll},
};
use bytes::Bytes;
use crate::client::{Error, SftpClient};
use crate::message::{self, Attrs, Handle};
mod close;
mod read;
mod seek;
mod write;
#[derive(Debug)]
pub struct File {
client: SftpClient,
handle: Option<Arc<Handle>>,
offset: u64,
pending: PendingOperation,
}
impl File {
pub fn new(client: SftpClient, handle: Handle) -> Self {
File {
client,
handle: Some(Arc::new(handle)),
offset: 0,
pending: PendingOperation::None,
}
}
pub const fn new_closed() -> Self {
File {
client: SftpClient::new_stopped(),
handle: None,
offset: 0,
pending: PendingOperation::None,
}
}
}
pub static FILE_CLOSED: File = File {
client: SftpClient::new_stopped(),
handle: None,
offset: 0,
pending: PendingOperation::None,
};
impl File {
pub fn stat(&self) -> impl Future<Output = Result<Attrs, Error>> + Send + Sync + 'static {
let future = if let Some(handle) = &self.handle {
Ok(self.client.request(message::FStat {
handle: Handle::clone(handle),
}))
} else {
Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"File was already closed",
)))
};
async move { future?.await }
}
pub fn set_stat(
&self,
attrs: Attrs,
) -> impl Future<Output = Result<(), Error>> + Send + Sync + 'static {
let future = if let Some(handle) = &self.handle {
Ok(self.client.request(message::FSetStat {
handle: Handle::clone(handle),
attrs,
}))
} else {
Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"File was already closed",
)))
};
async move { future?.await }
}
}
impl Clone for File {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
handle: self.handle.clone(),
offset: self.offset,
pending: PendingOperation::None,
}
}
}
type PendingFuture<T> = Pin<Box<dyn Future<Output = T> + Send + Sync + 'static>>;
enum PendingOperation {
None,
Read(PendingFuture<std::io::Result<Bytes>>),
Seek(PendingFuture<std::io::Result<u64>>),
Write(PendingFuture<std::io::Result<usize>>),
Close(PendingFuture<std::io::Result<()>>),
}
impl std::fmt::Debug for PendingOperation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => write!(f, "None"),
Self::Read(_) => write!(f, "Read(...)"),
Self::Seek(_) => write!(f, "Seek(...)"),
Self::Write(_) => write!(f, "Write(...)"),
Self::Close(_) => write!(f, "Close(...)"),
}
}
}
enum OperationResult {
None,
Read(std::io::Result<Bytes>),
Seek(std::io::Result<u64>),
Write(std::io::Result<usize>),
Close(std::io::Result<()>),
}
impl PendingOperation {
fn poll(&mut self, cx: &mut std::task::Context<'_>) -> Poll<OperationResult> {
let result = match self {
PendingOperation::None => OperationResult::None,
PendingOperation::Read(pending) => {
OperationResult::Read(ready!(pending.as_mut().poll(cx)))
}
PendingOperation::Seek(pending) => {
OperationResult::Seek(ready!(pending.as_mut().poll(cx)))
}
PendingOperation::Write(pending) => {
OperationResult::Write(ready!(pending.as_mut().poll(cx)))
}
PendingOperation::Close(pending) => {
OperationResult::Close(ready!(pending.as_mut().poll(cx)))
}
};
*self = PendingOperation::None;
Poll::Ready(result)
}
}