Qubit Progress
qubit-progress is a lifecycle-safe progress-reporting library for one long-running operation. It sends complete, immutable events to a Reporter: each event contains the operation phase, elapsed time, stable metric metadata, and the latest dynamic counts.
Why this crate?
Progress reporting is often coupled to a terminal progress bar or scattered across a copy loop as ad-hoc counters. That makes it difficult to send the same state to logs, JSON, a UI, or telemetry; it also makes consumers reconstruct state from deltas. Threaded work adds another problem: the code that owns the operation and the code that changes the counters are usually different.
This crate separates the two kinds of state. Configure each Metric—its stable ID, display name, and optional total—once at startup. Progress then owns the dynamic counts; cloneable metric handles apply validated lifecycle transitions and every event reads one internally consistent snapshot. Use MetricDelta for one atomic batch of additive lifecycle changes, and OperationAttributes for immutable string correlation metadata shared by every event. With multiple metrics, snapshots are consistent per metric rather than a globally atomic cross-metric view while the operation is running. Its consuming terminal methods permit at most one terminal event and prevent later reports in safe Rust; finish() requires zero active work and satisfied known totals, while finish_unchecked() is available for intentionally incomplete successful outcomes. Dropping or unwinding before a terminal call can still abandon an operation.
Event delivery is at-most-once: the crate returns reporter failures to the
caller and does not automatically retry them. Applications that retry at a
higher level must account for a reporter that accepted an event before
returning an error, which can make a retry duplicate the event; idempotent
reporter sinks should use the event's operation_id and sequence when
deduplicating.
Installation
[]
= "0.8"
Enable serde to serialize and deserialize event data, json-lines for
JsonLinesReporter (which includes serde), or log for LogReporter.
Basic use: copy a directory of files
Suppose an import command copies a known list of files. The operation declares the number of files once. Before each copy it marks one file active; after success it marks that file succeeded and reports the latest counts. TextReporter writes one complete line per event to standard error, but the same Progress code works with a custom reporter.
use ;
use ;
Started, every Running event, and Succeeded all carry the configured total. The application never repeats that fixed configuration, and a reporter never needs a previous event to understand the current state.
This example shows the success path. On failure or cancellation, send the matching terminal event before returning; see Close every operation.
Worker-thread use: automatically report shared copy state
For parallel or worker-driven work, let Progress own one scoped background reporter thread for the whole operation. Every worker updates a cloneable metric handle and shares the cloneable ProgressNotifier; the reporter thread coalesces notifications and delivers events. Call stop() before the terminal event: the scoped exclusive borrow prevents manual reporting or termination while the background reporter is active.
use ;
use ;
let reporter = new;
let mut progress = builder
.interval
.metric
.start?;
let files_metric = progress.metric.expect;
scope?;
progress.finish?;
# Ok::
Without .interval(...), the default is Duration::ZERO: workers call notify() after changes, and repeated notifications coalesce into at most one pending report. This example sets a positive interval, so the background reporter sends periodic heartbeats instead; notify() is a no-op and workers avoid notification synchronization work. The background reporter wakes only for its timer or stop(). Relative deadlines accept even Duration::MAX; that value simply has no future automatic due deadline.
Next steps
Read the user guide for the lifecycle model, validation rules, scheduling, automatic reporting, reporters, and error handling. API-level details are available on docs.rs.
Testing
# Run tests with the default feature set
# Run tests with all declared features
# Project CI checks
# Check code coverage
License
Copyright (c) 2025 - 2026. Haixing Hu. All rights reserved.
Licensed under the Apache License, Version 2.0. See LICENSE for the full license text.
Contributing
Contributions are welcome. Please follow the Rust API guidelines, keep public
API documentation and tests current, and run ./align-ci.sh to format code and
./ci-check.sh to satisfy CI requirements before submitting a pull request.
Author
Haixing Hu - Qubit Co. Ltd.
Repository: https://github.com/qubit-ltd/rs-progress