Documentation and interactive examples · API reference

Add cooperative cancellation and progress to a Rust library through one argument: &dyn Pulse. Your functions keep their ordinary inputs and results. The caller chooses whether to track progress, display it, or collect diagnostics.

Start with one function

This library function reports completed bytes and checks for cancellation. It needs only how-far; the application adds how-far-along to observe it.

use how_far::{prelude::*, StopReason};
use how_far_along::{Phase, PulseTree, Total, Unstoppable};

fn checksum(bytes: &[u8], pulse: &dyn Pulse) -> Result<u64, StopReason> {
    pulse.check()?;
    let mut sum = 0;
    for &byte in bytes {
        sum += u64::from(byte);
        pulse.step(1)?; // Count completed work, then check for cancellation.
    }
    Ok(sum)
}

let bytes = b"hello";
let tree = PulseTree::new(
    Phase::new("checksum", Total::Exact(bytes.len() as u64)), Unstoppable);
let observer = tree.observer();
let sum = checksum(bytes, &tree).finish_phase(tree)?;
assert_eq!(sum, 532);
assert_eq!(observer.snapshot().fraction(), Some(1.0));

The library reports work; the caller finishes the root with its result. .finish_phase(tree) records success, failure or cancellation and returns that same result. Keep it before ? so errors also reach the tracker. The observer can be retained by a UI or another thread while work runs.

Pass &how_far::NoPulse when you need neither tracking nor cancellation, or &how_far::StopOnly::borrowed(&token) for cancellation alone. A Pulse is also an enough::Stop, so existing stop-aware code can use the same handle. For tight loops, pulse.paced(64) batches reports and checks; finish with pace.finish() to check the last partial batch.

Add it to your project

Use the 0.1 release:

[dependencies]
how-far = "0.1.0"
how-far-along = "0.1.0"

For a library, keep only how-far in production dependencies; put the tracker in dev-dependencies when it is used only by tests and examples. All three crates need Rust 1.88. Core is no_std + alloc, has no feature flags, and depends only on enough. Dependency details cover diagnostics and matching an existing enough dependency.

Grow with the work

Start with checkpoints. Add structure when you have distinct operations to show:

Your workPattern
Different sequential operationsBind variables with Stages::define, then stages.begin(decode) and ordinary function calls. Example
Independent branches that joinstage.fork([specs...]) gives each branch a handle; your threads or async runtime schedule and join them. Example
The same operation over a runtime-sized listStages::new and begin_next() inside the loop. Example

Each active handle uses the same .finish_phase(handle)? result handoff. Stages need no closures; run and complete_with remain optional conveniences. The integration guide explains custom errors, optional stages, cancellation and caller-owned completion.

CrateUse it for
how-farLibrary-facing cancellation, counters, weighted stages and result handoffs. no_std + alloc.
how-far-alongApplication progress trees, snapshots, callbacks and optional display smoothing. no_std + alloc; std is the default.
how-far-reallyTests and tuning: find missing handoffs, checkpoint gaps and uneven progress. Requires std.

Try the cross-crate example from this checkout:

cargo run -p how-far-example-app --example integration

The quickstart and integration guide run as doctests. See design and API boundaries, testing and tuning, validation coverage, and compile-cost measurements.

License

Licensed under either of MIT or Apache-2.0, at your option.