use std::{
future::Future,
pin::Pin,
sync::Arc,
task::{ready, Poll},
};
use crate::message::{self, Attrs, Handle};
use crate::{
client::{Error, SftpClient},
message::Data,
};
use super::SftpFuture;
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) -> SftpFuture<Attrs> {
if let Some(handle) = &self.handle {
self.client.request(message::FStat {
handle: Handle::clone(handle),
})
} else {
SftpFuture::Error(Error::Io(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"File was already closed",
)))
}
}
pub fn set_stat(&self, attrs: Attrs) -> SftpFuture {
if let Some(handle) = &self.handle {
self.client.request(message::FSetStat {
handle: Handle::clone(handle),
attrs,
})
} else {
SftpFuture::Error(Error::Io(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"File was already closed",
)))
}
}
}
impl Clone for File {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
handle: self.handle.clone(),
offset: self.offset,
pending: PendingOperation::None,
}
}
}
enum PendingOperation {
None,
Read(SftpFuture<Data>),
Seek(SftpFuture<u64, i64>),
Write(SftpFuture<usize, usize>),
Close(SftpFuture),
}
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(Result<Data, Error>),
Seek(Result<u64, Error>),
Write(Result<usize, Error>),
Close(Result<(), Error>),
}
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!(Pin::new(pending).poll(cx)))
}
PendingOperation::Seek(pending) => {
OperationResult::Seek(ready!(Pin::new(pending).poll(cx)))
}
PendingOperation::Write(pending) => {
OperationResult::Write(ready!(Pin::new(pending).poll(cx)))
}
PendingOperation::Close(pending) => {
OperationResult::Close(ready!(Pin::new(pending).poll(cx)))
}
};
*self = PendingOperation::None;
Poll::Ready(result)
}
}