use std::io::{Read, Result as IoResult, Write};
use super::control::{SharedControl, lock};
use super::data_stream::DataStream;
use super::tls::TlsStream;
use crate::types::FtpResult;
#[must_use = "call `finish()` to close the data connection and read the transfer reply"]
#[derive(Debug)]
pub struct TransferStream<T>
where
T: TlsStream,
{
data: Option<DataStream<T>>,
control: SharedControl<T>,
}
impl<T> TransferStream<T>
where
T: TlsStream,
{
pub(super) fn new(data: DataStream<T>, control: SharedControl<T>) -> Self {
Self {
data: Some(data),
control,
}
}
pub fn get_ref(&self) -> &DataStream<T> {
self.data
.as_ref()
.expect("data stream is present until the transfer is finished")
}
pub fn get_mut(&mut self) -> &mut DataStream<T> {
self.data
.as_mut()
.expect("data stream is present until the transfer is finished")
}
pub fn finish(mut self) -> FtpResult<()> {
self.finalize()
}
pub(super) fn detach(mut self) -> DataStream<T> {
self.data
.take()
.expect("data stream is present until the transfer is finished")
}
fn is_finished(&self) -> bool {
self.data.is_none()
}
fn finalize(&mut self) -> FtpResult<()> {
let Some(data) = self.data.take() else {
return Ok(());
};
drop(data);
lock(&self.control).complete_transfer()
}
}
impl<T> Read for TransferStream<T>
where
T: TlsStream,
{
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
self.get_mut().read(buf)
}
}
impl<T> Write for TransferStream<T>
where
T: TlsStream,
{
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
self.get_mut().write(buf)
}
fn flush(&mut self) -> IoResult<()> {
self.get_mut().flush()
}
}
impl<T> Drop for TransferStream<T>
where
T: TlsStream,
{
fn drop(&mut self) {
if self.is_finished() {
return;
}
debug!("transfer stream dropped without finish(); finalizing the transfer");
if let Err(err) = self.finalize() {
warn!("failed to finalize a dropped transfer stream: {err}");
}
}
}