Expand description
§fast-down-api
A convenient, high-level wrapper around fast-down
that turns the pull/push engine into a few lines of async code: spawn a download,
drain progress events, resume after interruption, and cancel cooperatively.
- Concurrent, resumable downloads powered by the
fast-downengine (work-stealing, range requests). - Two layers of entry points: the fire-and-forget
download/resumewrappers, and the lower-levelplan/plan_resumepair that prefetches the remote and inspects the disk without writing a single byte — so you can preview the outcome and decide before committing. - Event stream: a single channel carries prefetch, disk allocation, per-worker progress, resume, rename, and lifecycle events. Every run ends with exactly one
Event::Terminated(TerminationReason). - Cooperative cancellation: cancelling mid-flight preserves the
.part/.fdfiles so you can resume later. - Configurable: threads, chunk size, write method (
Mmap/Std), proxies, headers, retries, disk pre-allocation, and more viaPartialConfig.
§Quick start
use fast_down_api::{create_cancellation_token, create_channel, download, Event, PartialConfig};
use std::path::PathBuf;
use url::Url;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 1. Channel for progress / lifecycle events, plus a cancellation token.
let (tx, rx) = create_channel();
let token = create_cancellation_token();
// 2. Configure the download. Every field is optional; unset fields
// fall back to Config::default(). (save_dir is required at runtime.)
let config = PartialConfig {
save_dir: Some(PathBuf::from("./downloads")),
overwrite: Some(true),
threads: Some(16),
..Default::default()
};
// 3. Start the download. This spawns a detached task and returns at once.
let url = Url::parse("https://example.com/large-file.bin")?;
download(url, config, tx, token.clone());
// 4. Drain events until the task finishes or is cancelled.
while let Ok(event) = rx.recv().await {
match event {
// Aggregated progress on a fixed cadence (Config::progress_emit_gap).
// Convenient for a progress bar — no need to re-accumulate ranges.
Event::Progress(sample) => {
// `downloaded`, `percent`, and `total` are pre-computed for you;
// the fields below are equivalent to deriving them from `progress`.
let written: u64 = sample.progress.iter().map(|r| r.end - r.start).sum();
assert_eq!(written, sample.downloaded);
let pct = if sample.total > 0 {
written * 100 / sample.total
} else {
0
};
// `eta` is the estimated remaining time, or `None` until a rate
// can be measured.
let eta_str = sample
.eta
.map_or_else(|| "?".to_string(), |d| format!("{d:?}"));
println!(
"progress: {pct}% ({:.1}%) {written}/{} bytes \
{} B/s (inst) {} B/s (avg) elapsed {:?} eta {eta_str}",
sample.percent, sample.total, sample.bps, sample.avg_bps, sample.elapsed
);
}
Event::PushProgress(p) => println!("wrote range: {p:?}"),
Event::Renamed(path) => {
println!("done -> {path:?}");
break;
}
Event::RenameFailed(e) => {
eprintln!("rename failed: {e}");
break;
}
Event::ResumeError(e) => eprintln!("resume error: {e}"),
Event::Allocating(size) => println!("pre-allocating {size} bytes on disk"),
Event::AllocError(e) => eprintln!("pre-allocation failed (continuing): {e}"),
Event::Terminated(reason) => {
println!("terminated: {reason:?}");
break;
}
_ => {}
}
}
// 5. Draining `rx` above already waits for completion: the loop ends when the
// task drops its last sender, so no separate join handle is needed.
Ok(())
}§Resuming an interrupted download
resume targets the existing .part file. The url argument is optional: pass
None to re-use the initial URL already recorded in the .fd state file, so you
can resume purely from the .part path. If the download cannot be continued (no
.fd state, no range support, or the remote file changed) it emits
Event::ResumeError and does not silently restart — unlike download, which
auto-resumes when it can and otherwise falls back to a fresh download. With
url = None and no resolvable URL available it reports StateError::NoUrl.
use fast_down_api::{PartialConfig, create_cancellation_token, create_channel, resume};
use url::Url;
let url = Url::parse("https://example.com/large-file.bin")?;
let config = PartialConfig::default();
let (tx, _rx) = create_channel();
let token = create_cancellation_token();
resume(
"./downloads/large-file.bin.part", // the .part file from a previous run
Some(url), // pass None to reuse the URL stored in the .fd
config,
tx,
token,
);§Cancelling cooperatively
use fast_down_api::create_cancellation_token;
let token = create_cancellation_token();
token.cancel(); // stops fetching, keeps .part / .fd so you can resume later§Two-phase planning (inspect before you commit)
plan and plan_resume do everything download / resume do except touch
the disk: they prefetch the remote metadata, resolve the output path, and probe
the .fd / .part pair left by a previous run. The returned DownloadPlan
tells you what starting it would do — DownloadPlan::resume_outcome reports
Resumable, Fresh, or Mismatch — and nothing is created until you call one
of its start methods. Dropping the plan abandons the download with no side
effects.
use fast_down_api::{
PartialConfig, ResumeOutcome, create_cancellation_token, create_channel, plan,
};
use url::Url;
let url = Url::parse("https://example.com/large-file.bin")?;
let config = PartialConfig::default();
let (tx, _rx) = create_channel();
let token = create_cancellation_token();
let plan = plan(url, config.clone(), tx.clone(), token.clone()).await?;
match plan.resume_outcome() {
ResumeOutcome::Resumable { .. } => println!("will continue from a previous run"),
ResumeOutcome::Fresh => println!("will download the whole file"),
ResumeOutcome::Mismatch(e) => println!("stale state: {e} (use start_forced_resume)"),
}
// Commit when you're ready. Each start method emits exactly one `Event::Terminated`.
plan.start().await; // resume if possible, else fresh (or refuse for plan_resume)
// plan.start_fresh().await; // ignore any saved progress and re-download
// plan.start_forced_resume().await; // continue from a mismatched state when only identity changedplan_resume takes a .part path instead of a URL and hard-refuses a
Mismatch (sending Event::ResumeError + TerminationReason::Failed) rather
than restarting — because the caller asked to continue one specific file, not to
fetch it again. Pass a url to re-fetch the metadata, or None to reuse the URL
recorded in the .fd.
§API overview
| Item | Purpose |
|---|---|
download | Start a download; auto-resume when a valid .fd + .part exist, else fresh. Observe completion by draining the Rx from create_channel until it disconnects. |
resume | Resume a specific .part file; hard-error (Event::ResumeError) if it can’t. Completion is observed the same way, by draining Rx. |
plan | Prefetch + probe the disk and return a DownloadPlan without writing anything; start it only when ready. |
plan_resume | Like plan but targets a specific .part; refuses a Mismatch instead of re-downloading. |
DownloadPlan | A prepared, not-yet-started download. Inspect with resume_outcome, then call start / start_fresh / start_forced_resume. |
create_channel | Create the (Tx, Rx) event channel. |
create_cancellation_token | Create a CancellationToken for cooperative cancellation. |
Event | The event enum delivered over the channel. |
PartialConfig | Layered, optional configuration for a download. |
StateError | Errors surfaced via Event::ResumeError. |
§How resume works
During a download the engine periodically persists a .fd state file next to
the .part partial file. That state records the byte ranges already written
(downloaded_chunk) and the remote file identity (etag / last_modified /
size). On the next run:
downloadloads the.fd, validates it still matches the remote (size + identity), and — if the.partfile is present — emitsEvent::Resumedand continues from the recorded offset.- If validation fails (remote changed) or there is no
.part, it starts fresh. resumeapplies the same checks but, instead of falling back, reportsEvent::ResumeError.
Cancellation leaves both files in place, so a later resume (or download) can pick up exactly where it stopped.
Every run — whether it completes, is cancelled, stops incomplete, or fails —
ends with exactly one Event::Terminated(TerminationReason) as the last event
on the channel, so draining Rx until Terminated is the reliable way to know
a run has finished.
§License
MIT — see LICENSE.
Re-exports§
pub use fast_down;
Structs§
- Cancellation
Token - A token which can be used to signal a cancellation request to one or more tasks.
- Config
- Configuration for a download task.
- Download
Plan - A prepared, not-yet-started download.
- Download
State - On-disk state for an in-progress download, backing resume support.
- Download
State Inner - Full (resolved) download state that is serialized into the
.fdfile. - Partial
Config - Partial
Download State Inner - Progress
Sample - Computed aggregate view of the current download progress, carried by
Event::Progress. - Resume
Info - 已保存、可续传下载的只读快照,由
DownloadState::snapshot产出。
Enums§
- Event
- Events emitted by a download run, consumed through the crossfire channel
returned by
crate::create_channel. - Plan
Error - Why a download plan could not be produced.
- Resume
Outcome - What the disk probe found for the download a
DownloadPlandescribes. - State
Error - Errors that can occur when an explicit
resumecannot continue an interrupted download. - Termination
Reason - How a download run ended, carried by
Event::Terminated. - Write
Method - File write method for downloaded data.
Functions§
- create_
cancellation_ token - Create a new
CancellationTokenfor cooperative cancellation of a download. - create_
channel - Create a new unbounded event channel for receiving download progress events.
- download
- Spawn a detached background download task that resumes automatically when possible.
- download_
from_ fd - Spawn a detached task that downloads from a
.fdstate file used as a download manifest. - peek_
resume - Read a previously-saved download state from disk without contacting the network. The returned state is not validated against the current remote.
- plan
- Prepare a download without touching the filesystem.
- plan_
from_ fd - Prepare a download driven by a
.fdstate file used as a download manifest. - plan_
resume - Prepare a resume of an interrupted download from its
.partfile. - prefetch
- Resolve the remote file’s metadata, retrying up to
Config::retry_times. - resume
- Spawn a detached task that resumes a previously interrupted download from its
.partfile.