use std::collections::BTreeSet;
use crate::error::{InnerErrorCode, MeowError};
use super::{CompletionRequest, PresignedUploadPart};
#[derive(Debug, Clone)]
pub struct PresignedMultipartUploadPlan {
pub upload_id: Option<String>,
pub total_size: u64,
pub chunk_size: u64,
pub parts: Vec<PresignedUploadPart>,
pub complete_request: Option<CompletionRequest>,
pub abort_request: Option<CompletionRequest>,
pub refresh_before_secs: u64,
}
impl PresignedMultipartUploadPlan {
pub fn new(total_size: u64, chunk_size: u64, parts: Vec<PresignedUploadPart>) -> Self {
Self {
upload_id: None,
total_size,
chunk_size,
parts,
complete_request: None,
abort_request: None,
refresh_before_secs: 60,
}
}
pub fn with_upload_id(mut self, upload_id: impl Into<String>) -> Self {
self.upload_id = Some(upload_id.into());
self
}
pub fn with_complete_request(mut self, req: CompletionRequest) -> Self {
self.complete_request = Some(req);
self
}
pub fn with_abort_request(mut self, req: CompletionRequest) -> Self {
self.abort_request = Some(req);
self
}
pub fn with_refresh_before_secs(mut self, secs: u64) -> Self {
self.refresh_before_secs = secs;
self
}
pub fn validate(&self) -> Result<(), MeowError> {
if self.chunk_size == 0 {
return Err(MeowError::from_code_str(
InnerErrorCode::ParameterEmpty,
"presigned plan chunk_size must be greater than zero",
));
}
if self.total_size > 0 && self.parts.is_empty() {
return Err(MeowError::from_code_str(
InnerErrorCode::ParameterEmpty,
"presigned plan parts must not be empty for non-empty upload",
));
}
let mut offsets = BTreeSet::new();
for part in &self.parts {
if part.size == 0 {
return Err(MeowError::from_code(
InnerErrorCode::InvalidRange,
format!("presigned part {} has zero size", part.part_number),
));
}
let end = part.offset.checked_add(part.size).ok_or_else(|| {
MeowError::from_code(
InnerErrorCode::InvalidRange,
format!("presigned part {} range overflow", part.part_number),
)
})?;
if end > self.total_size {
return Err(MeowError::from_code(
InnerErrorCode::InvalidRange,
format!(
"presigned part {} out of range: end={} total={}",
part.part_number, end, self.total_size
),
));
}
if !offsets.insert(part.offset) {
return Err(MeowError::from_code(
InnerErrorCode::InvalidRange,
format!("duplicate presigned part offset: {}", part.offset),
));
}
}
Ok(())
}
pub(crate) fn part_for_offset(&self, offset: u64) -> Option<&PresignedUploadPart> {
self.parts.iter().find(|p| p.offset == offset)
}
}