use super::{ checksum::{streamed_checksum_verification, usize_to_u64}, model::{ HttpCompletionModel, HttpCompletionState, HttpResponseModel, HttpRetryAttemptDetailModel, HttpSegmentProgressModel, HttpTransferProgressModel, HttpTransferTaskModel, ResponseBody, }, }; impl HttpTransferTaskModel { #[must_use] /// Returns the effective requested offset for the task. pub fn requested_offset(&self) -> u64 { self.resume_state .as_ref() .map(|state| state.requested_offset) .or_else(|| self.request.range.as_ref().map(|range| range.start)) .unwrap_or_default() } #[must_use] /// Returns retry attempts enriched with resume and range context. pub fn retry_attempt_details(&self) -> Vec { let requested_offset = self.requested_offset(); let requested_range = self.request.range; let accepted_offset = self .resume_state .as_ref() .and_then(|state| state.accepted_offset); let resume_state = self.resume_state; self.retry_attempts .iter() .copied() .map(|base| HttpRetryAttemptDetailModel { base, requested_range, requested_offset, accepted_offset, resume_state, }) .collect() } #[must_use] /// Builds a progress snapshot from the current task and optional response. pub fn progress_snapshot( &self, response: Option<&HttpResponseModel>, ) -> HttpTransferProgressModel { let completion = response.map(HttpResponseModel::completion_model); let completed_offset = response.map(HttpResponseModel::completed_length); let (accepted_offset, resumed) = response.map_or_else( || { ( self.resume_state .as_ref() .and_then(|state| state.accepted_offset), self.resume_state .as_ref() .is_some_and(|state| state.resumed), ) }, |response| { if response.status == 206 { let accepted_offset = response .content_range .as_ref() .and_then(|range| (!range.is_unsatisfied()).then_some(range.start)); ( accepted_offset, accepted_offset.is_some() || self .resume_state .as_ref() .is_some_and(|state| state.resumed), ) } else { (None, false) } }, ); HttpTransferProgressModel { task_id: self.task_id.clone(), request: self.request.clone(), segment: HttpSegmentProgressModel { requested_range: self.request.range, requested_offset: self.requested_offset(), accepted_offset, completed_offset, resumed, }, retry_attempts: self.retry_attempt_details(), max_connections: self.max_connections, checksum_hook: self.checksum_hook.clone(), completion, } } } impl HttpResponseModel { #[must_use] /// Returns the total payload length when the response exposes it. pub fn total_length(&self) -> Option { self.content_range .as_ref() .and_then(|range| range.total_size) .or_else(|| match &self.body { ResponseBody::Inline(bytes) => Some(usize_to_u64(bytes.len())), ResponseBody::Streamed { expected_len, observed_len, .. } => expected_len.or(*observed_len), ResponseBody::Empty => None, }) } #[must_use] /// Returns the completed payload length represented by the response. pub fn completed_length(&self) -> u64 { self.content_range .as_ref() .map(super::model::ContentRangeSpec::completed_length) .or_else(|| match &self.body { ResponseBody::Inline(bytes) => Some(usize_to_u64(bytes.len())), ResponseBody::Streamed { observed_len, .. } => *observed_len, ResponseBody::Empty => Some(0), }) .unwrap_or_default() } #[must_use] /// Returns inline body bytes when they are retained in memory. pub fn body_bytes(&self) -> Option<&[u8]> { match &self.body { ResponseBody::Empty => Some(&[]), ResponseBody::Inline(bytes) => Some(bytes), ResponseBody::Streamed { .. } => None, } } #[must_use] /// Derives a completion summary from the response payload and metadata. pub fn completion_model(&self) -> HttpCompletionModel { let total_length = self.total_length(); let completed_length = self.completed_length(); let checksum_seen = self.checksum.is_some(); let checksum_verified = self.checksum.as_ref().is_some_and(|checksum| { self.body_bytes() .and_then(|bytes| checksum.verify_payload(bytes)) .or_else(|| streamed_checksum_verification(checksum, &self.body)) .unwrap_or_else(|| checksum.is_verified()) }); let terminal_success = (200..300).contains(&self.status); let complete_enough = total_length.is_none_or(|total| completed_length >= total); let state = if !terminal_success { HttpCompletionState::Incomplete } else if checksum_verified && complete_enough { HttpCompletionState::Verified } else if complete_enough { HttpCompletionState::Complete } else { HttpCompletionState::Partial }; HttpCompletionModel { state, total_length, completed_length, partial_content: self.partial_content, terminal_success, checksum_seen, checksum_verified, } } }