Add download lifecycle controls

This commit is contained in:
2026-05-07 21:36:39 -04:00
parent f93943ccfe
commit 0ed8c09395
5 changed files with 261 additions and 3 deletions
+81
View File
@@ -49,6 +49,52 @@ impl DownloadEntry {
})
}
pub fn pause(&mut self) -> Result<(), DomainError> {
self.require_state("pause", &[DownloadState::InProgress])?;
self.state = DownloadState::Paused;
Ok(())
}
pub fn resume(&mut self) -> Result<(), DomainError> {
self.require_state("resume", &[DownloadState::Paused])?;
self.state = DownloadState::InProgress;
Ok(())
}
pub fn cancel(&mut self) -> Result<(), DomainError> {
self.require_state("cancel", &[DownloadState::InProgress, DownloadState::Paused])?;
self.state = DownloadState::Cancelled;
Ok(())
}
pub fn retry(&mut self) -> Result<(), DomainError> {
self.require_state("retry", &[DownloadState::Cancelled, DownloadState::Failed])?;
self.state = DownloadState::InProgress;
self.received_bytes = 0;
Ok(())
}
pub fn update_progress(&mut self, received_bytes: u64) -> Result<(), DomainError> {
self.require_state("update progress", &[DownloadState::InProgress])?;
self.validate_received_bytes(received_bytes)?;
self.received_bytes = received_bytes;
Ok(())
}
pub fn complete(&mut self, received_bytes: u64) -> Result<(), DomainError> {
self.require_state("complete", &[DownloadState::InProgress])?;
self.validate_received_bytes(received_bytes)?;
self.received_bytes = received_bytes;
self.state = DownloadState::Completed;
Ok(())
}
pub fn fail(&mut self) -> Result<(), DomainError> {
self.require_state("fail", &[DownloadState::InProgress, DownloadState::Paused])?;
self.state = DownloadState::Failed;
Ok(())
}
#[must_use]
pub fn id(&self) -> &DownloadId {
&self.id
@@ -88,4 +134,39 @@ impl DownloadEntry {
pub fn started_at(&self) -> SystemTime {
self.started_at
}
fn require_state(
&self,
action: &'static str,
allowed_states: &[DownloadState],
) -> Result<(), DomainError> {
if allowed_states.iter().any(|state| state == &self.state) {
return Ok(());
}
Err(DomainError::InvalidDownloadTransition { action, state: self.state.as_str() })
}
fn validate_received_bytes(&self, received_bytes: u64) -> Result<(), DomainError> {
if let Some(total_bytes) = self.total_bytes
&& received_bytes > total_bytes
{
return Err(DomainError::InvalidDownloadProgress { received_bytes, total_bytes });
}
Ok(())
}
}
impl DownloadState {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::InProgress => "in_progress",
Self::Paused => "paused",
Self::Completed => "completed",
Self::Cancelled => "cancelled",
Self::Failed => "failed",
}
}
}
+6
View File
@@ -10,4 +10,10 @@ pub enum DomainError {
#[error("invalid command query")]
InvalidCommand,
#[error("cannot {action} download while state is {state}")]
InvalidDownloadTransition { action: &'static str, state: &'static str },
#[error("download progress {received_bytes} exceeds total {total_bytes}")]
InvalidDownloadProgress { received_bytes: u64, total_bytes: u64 },
}