backbone_bucket/domain/entity/
upload_session.ext.rs1#[allow(unused_imports)]
4use super::*;
5
6impl UploadSession {
7
8 pub fn is_expired(&self) -> bool {
14 self.expires_at <= Utc::now()
15 }
16
17 pub fn add_part(&mut self, part_number: i32, etag: String) -> Result<(), UploadError> {
19 if self.is_expired() {
20 return Err(UploadError::ValidationFailed("Upload session has expired".to_string()));
21 }
22
23 if self.completed_parts.contains(&part_number) {
24 return Err(UploadError::Conflict(format!("Part {} already uploaded", part_number)));
25 }
26
27 if self.uploaded_chunks >= self.total_chunks {
28 return Err(UploadError::ValidationFailed("All chunks already uploaded".to_string()));
29 }
30
31 self.completed_parts.push(part_number);
32 self.uploaded_chunks += 1;
33
34 let etags = self.part_etags.get_or_insert_with(|| serde_json::json!({}));
36 if let Some(obj) = etags.as_object_mut() {
37 obj.insert(part_number.to_string(), serde_json::Value::String(etag));
38 }
39
40 self.status = UploadStatus::Uploading;
41 self.metadata.updated_at = Some(Utc::now());
42
43 Ok(())
44 }
45
46 pub fn is_complete(&self) -> bool {
48 self.uploaded_chunks >= self.total_chunks
49 }
50
51 pub fn calculate_progress(&self) -> i32 {
53 if self.total_chunks <= 0 {
54 return 0;
55 }
56 ((self.uploaded_chunks as f64 / self.total_chunks as f64) * 100.0) as i32
57 }
58
59 pub fn remaining_chunks(&self) -> i32 {
61 (self.total_chunks - self.uploaded_chunks).max(0)
62 }
63
64 pub fn can_resume(&self) -> bool {
66 if self.is_expired() {
67 return false;
68 }
69 matches!(self.status, UploadStatus::Initiated | UploadStatus::Uploading)
70 }
71
72 pub fn mark_complete(&mut self) -> Result<(), UploadError> {
74 if !self.is_complete() {
75 return Err(UploadError::ValidationFailed(
76 format!("Upload not complete: {}/{} chunks uploaded", self.uploaded_chunks, self.total_chunks),
77 ));
78 }
79 self.status = UploadStatus::Completed;
80 self.metadata.updated_at = Some(Utc::now());
81 Ok(())
82 }
83
84 pub fn mark_failed(&mut self, error: String) -> Result<(), UploadError> {
86 self.status = UploadStatus::Failed;
87 let etags = self.part_etags.get_or_insert_with(|| serde_json::json!({}));
89 if let Some(obj) = etags.as_object_mut() {
90 obj.insert("_error".to_string(), serde_json::Value::String(error));
91 }
92 self.metadata.updated_at = Some(Utc::now());
93 Ok(())
94 }
95
96 pub fn check_invariants(&self) -> Result<(), Vec<&'static str>> {
98 let mut errors = Vec::new();
99 if self.uploaded_chunks > self.total_chunks {
101 errors.push("uploaded_chunks must not exceed total_chunks");
102 }
103 if self.uploaded_chunks != self.completed_parts.len() as i32 {
105 errors.push("uploaded_chunks must equal completed_parts length");
106 }
107 if self.file_size <= 0 {
109 errors.push("file_size must be greater than 0");
110 }
111 if self.total_chunks <= 0 {
112 errors.push("total_chunks must be greater than 0");
113 }
114 if self.chunk_size < 5 * 1024 * 1024 {
116 errors.push("chunk_size must be at least 5MB (5242880 bytes)");
117 }
118 if errors.is_empty() { Ok(()) } else { Err(errors) }
119 }
120}