coven_storage/
transfer.rs1use crate::{ObjectPath, Storage, StorageError, StorageFailure};
2use std::future::Future;
3
4pub const BUFFERED_REQUEST_LIMIT: u64 = 32 * 1024 * 1024;
10
11pub fn goes_in_parts(storage: &(impl Storage + ?Sized), size: u64) -> bool {
18 size > storage.single_request_limit().min(BUFFERED_REQUEST_LIMIT)
19}
20
21pub(crate) async fn upload_bytes(
26 storage: &crate::StorageConnection<impl crate::ProviderOps + ?Sized>,
27 path: &ObjectPath,
28 bytes: &[u8],
29 sent: &crate::ByteProgress,
30 single_request: impl Future<Output = Result<Option<crate::StoredObject>, StorageError>> + Send,
31) -> Result<Option<crate::StoredObject>, StorageError> {
32 if !goes_in_parts(storage, bytes.len() as u64) {
33 return single_request.await;
34 }
35 let mut session = storage
36 .begin_upload_checked(&crate::UploadTarget::Create(path.clone()), bytes.len() as u64)
37 .await?;
38 let upload = async {
39 while let Some(part) = session.parts_to_send().first().cloned() {
40 let confirmed = session.confirmed_bytes();
41 let span = usize::try_from(part.start)
42 .and_then(|start| Ok(start..usize::try_from(part.end)?))
43 .map_err(|error| StorageFailure::InvalidPart.with_source(error))?;
44 let part_bytes = bytes.get(span).ok_or(StorageFailure::InvalidPart)?;
45 let permit = storage.part_permit(&mut session, part_bytes)?;
46 storage.send_permitted(permit, &mut session, sent).await?;
47 if session.confirmed_bytes() <= confirmed {
48 return Err(StorageFailure::Protocol.with_source("upload did not advance"));
49 }
50 }
51 storage.finish_upload_checked(&mut session).await
52 }
53 .await;
54 match upload {
55 Ok(()) => Ok(None),
56 Err(operation) => {
57 with_cleanup(Err(operation), storage.abort_upload_checked(&session).await)
58 }
59 }
60}
61
62pub(crate) fn with_cleanup<T>(
63 operation: Result<T, StorageError>,
64 cleanup: Result<(), StorageError>,
65) -> Result<T, StorageError> {
66 match (operation, cleanup) {
67 (Ok(value), Ok(())) => Ok(value),
68 (Err(error), Ok(())) | (Ok(_), Err(error)) => Err(error),
69 (Err(operation), Err(cleanup)) => Err(StorageError::Cleanup {
70 operation: Box::new(operation),
71 cleanup: Box::new(cleanup),
72 }),
73 }
74}
75
76pub(crate) fn check_single_request(size: u64, limit: u64) -> Result<(), StorageError> {
77 if size > limit {
78 return Err(StorageFailure::SingleRequestTooLarge { size, limit }.into());
79 }
80 Ok(())
81}
82
83#[cfg(test)]
84#[path = "transfer_tests.rs"]
85mod tests;