use std::{
pin::Pin,
task::{ready, Poll},
};
use crate::{
client::{Error, SftpReply, SftpRequest},
message::{Attrs, FStat, Handle},
};
use super::{File, OperationResult, PendingOperation};
impl tokio::io::AsyncSeek for File {
fn start_seek(mut self: Pin<&mut Self>, position: std::io::SeekFrom) -> std::io::Result<()> {
if let PendingOperation::None = self.pending {
match position {
std::io::SeekFrom::Start(n) => {
self.offset = n;
}
std::io::SeekFrom::End(i) => {
let Some(handle) = &self.handle else {
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"File was closed",
));
};
let handle = Handle::clone(handle);
self.pending = PendingOperation::Seek(self.client.request_with(
FStat { handle }.to_request_message(),
i,
|i, msg| match Attrs::from_reply_message(msg)?.size {
Some(n) => match n.checked_add_signed(i) {
Some(n) => Ok(n),
None => Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Would seek to negative position",
))),
},
None => Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Unable to seek from the end of file: could not get file size",
))),
},
));
}
std::io::SeekFrom::Current(i) => match self.offset.checked_add_signed(i) {
Some(n) => self.offset = n,
None => {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Would seek to negative position",
))
}
},
}
Ok(())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::WouldBlock,
"A pending operation must complete before seek",
))
}
}
fn poll_complete(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<std::io::Result<u64>> {
match ready!(self.pending.poll(cx)) {
OperationResult::Seek(seek) => {
if let Ok(n) = seek {
self.offset = n;
}
Poll::Ready(seek.map_err(Into::into))
}
_ => Poll::Ready(Ok(self.offset)),
}
}
}