feat(download): resume interrupted transfers

This commit is contained in:
Michel de Graaf
2026-09-09 15:49:35 +02:00
parent 7f84998c2e
commit 926c1c6697
9 changed files with 1032 additions and 332 deletions
+79 -1
View File
@@ -61,13 +61,45 @@ impl DownloadStore {
let Some(download) = self.get_by_token_mut(token) else {
return false;
};
if matches!(download.status, DownloadStatus::Cancelled) {
if download.status.is_terminal()
|| matches!(
(&download.status, &status),
(
DownloadStatus::Paused { .. },
DownloadStatus::InProgress { .. }
)
)
{
return false;
}
download.status = status;
true
}
/// Atomically reserve a queued download for the peer that owns it.
///
/// Every file-connection direction uses this before opening a partial, so
/// two sockets cannot both observe `Queued` and write the same target.
pub fn claim_for_peer(
&mut self,
token: u32,
username: &str,
) -> Option<Download> {
let download = self.get_by_token_mut(token)?;
if download.username != username
|| !matches!(download.status, DownloadStatus::Queued)
{
return None;
}
download.status = DownloadStatus::InProgress {
bytes_downloaded: 0,
total_bytes: download.size,
speed_bytes_per_sec: 0.0,
};
Some(download.clone())
}
pub fn cancel_by_file(
&mut self,
username: &str,
@@ -373,6 +405,52 @@ mod tests {
));
}
#[test]
fn progress_cannot_implicitly_resume_a_paused_download() {
let mut store = DownloadStore::new();
store.add(make_download(
1,
DownloadStatus::Paused {
bytes_downloaded: 25,
total_bytes: 100,
},
));
assert!(!store.update_status(
1,
DownloadStatus::InProgress {
bytes_downloaded: 50,
total_bytes: 100,
speed_bytes_per_sec: 10.0,
},
));
assert!(matches!(
store.get_by_token(1).unwrap().status,
DownloadStatus::Paused {
bytes_downloaded: 25,
total_bytes: 100
}
));
}
#[test]
fn a_download_can_only_be_claimed_once_and_by_its_peer() {
let mut store = DownloadStore::new();
store.add(make_download(1, DownloadStatus::Queued));
assert!(store.claim_for_peer(1, "other").is_none());
let claimed = store.claim_for_peer(1, "peer").unwrap();
assert!(matches!(
claimed.status,
DownloadStatus::InProgress {
bytes_downloaded: 0,
total_bytes: 100,
speed_bytes_per_sec: 0.0
}
));
assert!(store.claim_for_peer(1, "peer").is_none());
}
#[test]
fn remove_queued_skips_active_downloads() {
let mut store = DownloadStore::new();