use std::future::Future;
use std::ops::DerefMut;
use crate::raw::*;
use crate::*;
pub type Copier = Box<dyn CopyDyn>;
pub trait Copy: Unpin + Send + Sync {
fn next(&mut self) -> impl Future<Output = Result<Option<usize>>> + MaybeSend;
fn close(&mut self) -> impl Future<Output = Result<Metadata>> + MaybeSend;
fn abort(&mut self) -> impl Future<Output = Result<()>> + MaybeSend;
}
impl Copy for () {
async fn next(&mut self) -> Result<Option<usize>> {
Ok(None)
}
async fn close(&mut self) -> Result<Metadata> {
Ok(Metadata::default())
}
async fn abort(&mut self) -> Result<()> {
Ok(())
}
}
pub struct OneShotCopier {
factory: Option<Box<dyn FnMut() -> BoxedStaticFuture<Result<Metadata>>>>,
fut: Option<BoxedStaticFuture<Result<Metadata>>>,
meta: Option<Metadata>,
consumed: bool,
}
unsafe impl Sync for OneShotCopier {}
unsafe impl Send for OneShotCopier {}
impl OneShotCopier {
pub fn new(fut: impl Future<Output = Result<Metadata>> + MaybeSend + 'static) -> Self {
Self {
factory: None,
fut: Some(Box::pin(fut)),
meta: None,
consumed: false,
}
}
pub fn new_with<F, Fut>(mut factory: F) -> Self
where
F: FnMut() -> Fut + 'static,
Fut: Future<Output = Result<Metadata>> + MaybeSend + 'static,
{
Self {
factory: Some(Box::new(move || {
Box::pin(factory()) as BoxedStaticFuture<Result<Metadata>>
})),
fut: None,
meta: None,
consumed: false,
}
}
pub fn completed() -> Self {
Self {
factory: None,
fut: None,
meta: Some(Metadata::default()),
consumed: true,
}
}
}
impl Copy for OneShotCopier {
async fn next(&mut self) -> Result<Option<usize>> {
if self.meta.is_none() {
self.close().await?;
}
Ok(None)
}
async fn close(&mut self) -> Result<Metadata> {
if let Some(meta) = self.meta.clone() {
return Ok(meta);
}
if self.consumed {
return Err(Error::new(
ErrorKind::Unexpected,
"one-shot copier has already been consumed",
)
.set_persistent());
}
if self.fut.is_none()
&& let Some(factory) = self.factory.as_mut()
{
self.fut = Some(factory());
}
if let Some(fut) = self.fut.take() {
match fut.await {
Ok(meta) => {
self.consumed = true;
self.meta = Some(meta.clone());
return Ok(meta);
}
Err(err) => {
if self.factory.is_none() {
self.consumed = true;
return Err(err.set_persistent());
}
return Err(err);
}
}
}
self.consumed = true;
Err(Error::new(
ErrorKind::Unexpected,
"one-shot copier has no future to drive",
)
.set_persistent())
}
async fn abort(&mut self) -> Result<()> {
self.factory = None;
self.fut = None;
self.meta = None;
self.consumed = true;
Ok(())
}
}
pub trait CopyDyn: Unpin + Send + Sync {
fn next_dyn(&mut self) -> BoxedFuture<'_, Result<Option<usize>>>;
fn close_dyn(&mut self) -> BoxedFuture<'_, Result<Metadata>>;
fn abort_dyn(&mut self) -> BoxedFuture<'_, Result<()>>;
}
impl<T: Copy + ?Sized> CopyDyn for T {
fn next_dyn(&mut self) -> BoxedFuture<'_, Result<Option<usize>>> {
Box::pin(self.next())
}
fn close_dyn(&mut self) -> BoxedFuture<'_, Result<Metadata>> {
Box::pin(self.close())
}
fn abort_dyn(&mut self) -> BoxedFuture<'_, Result<()>> {
Box::pin(self.abort())
}
}
impl<T: CopyDyn + ?Sized> Copy for Box<T> {
async fn next(&mut self) -> Result<Option<usize>> {
self.deref_mut().next_dyn().await
}
async fn close(&mut self) -> Result<Metadata> {
self.deref_mut().close_dyn().await
}
async fn abort(&mut self) -> Result<()> {
self.deref_mut().abort_dyn().await
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use super::*;
#[tokio::test]
async fn one_shot_copier_rebuilds_future_after_error() {
let attempts = Arc::new(AtomicUsize::new(0));
let retry_attempts = attempts.clone();
let mut copier = OneShotCopier::new_with(move || {
let attempts = retry_attempts.clone();
async move {
if attempts.fetch_add(1, Ordering::Relaxed) == 0 {
return Err(
Error::new(ErrorKind::Unexpected, "temporary copy failure").set_temporary()
);
}
Ok(Metadata::new(EntryMode::FILE))
}
});
assert!(copier.close().await.unwrap_err().is_temporary());
assert_eq!(copier.close().await.unwrap().mode(), EntryMode::FILE);
assert_eq!(attempts.load(Ordering::Relaxed), 2);
}
#[tokio::test]
async fn one_shot_copier_does_not_succeed_after_consumed_error() {
let mut copier = OneShotCopier::new(async {
Err(Error::new(ErrorKind::Unexpected, "copy failure").set_temporary())
});
let err = copier.close().await.unwrap_err();
assert_eq!(err.kind(), ErrorKind::Unexpected);
assert!(err.is_persistent());
let err = copier.close().await.unwrap_err();
assert_eq!(err.kind(), ErrorKind::Unexpected);
assert!(err.is_persistent());
}
}