Qubit Progress
Generic progress reporting abstractions for Qubit Rust libraries and applications.
Overview
Qubit Progress models progress as immutable, self-describing events. Each event carries a metric schema, lifecycle phase, optional stage information, metric counters, and elapsed time. Reporters receive complete event snapshots and can render them as human-readable text, logs, JSON, or application-specific records.
Use this crate when you need:
- stable metric definitions for an operation, such as files, bytes, entries, or tasks;
u64counters grouped by metric id;- lifecycle phases such as
started,running,finished,failed, andcanceled; - optional stage metadata for multi-step operations;
- operation-scoped reporting with configurable running-report intervals;
- background running reporters for worker-driven operations;
- serde-serializable progress events suitable for logs, agents, and structured consumers.
For detailed usage, extension examples, and reporter design guidance, see the User Guide. API reference documentation is available on docs.rs.
Installation
[]
= "0.5"
Quick Example
use Duration;
use ;
let schema = new;
let event = builder
.running
.stage_named
.counter
.counter
.elapsed
.build;
assert_eq!;
assert_eq!;
Main Capabilities
Schema and Metrics
ProgressSchema defines the metric dimensions that may appear in a progress
event stream. A metric has a stable id for structured data and a human-readable
name for display output.
| Type | Purpose |
|---|---|
ProgressSchema |
metric definitions for one logical operation |
ProgressMetric |
stable metric id plus display name |
ProgressCounter |
u64 counts for one metric id |
ProgressMetricSnapshot |
one metric counter flattened with event phase, stage, and elapsed time |
ProgressStage |
optional multi-stage operation metadata |
A schema can contain multiple metrics, for example entries and bytes, so a
single event can report logical item progress and byte progress together without
mixing their units.
Events and Counters
A ProgressEvent is an immutable snapshot. It contains:
| Field | Purpose |
|---|---|
schema |
metric definitions carried with the event |
phase |
lifecycle state: started, running, finished, failed, or canceled |
stage |
optional multi-stage operation metadata |
counters |
one or more ProgressCounter values grouped by metric_id |
elapsed |
elapsed Duration, serialized by qubit-serde as strings such as 110ms |
Use ProgressEvent::builder(schema) to build events directly:
use Duration;
use ;
let event = builder
.running
.counter
.elapsed
.build;
assert_eq!;
Operation-Scoped Progress
A Progress instance is scoped to one logical operation. Do not mix unrelated
operations into one reporter stream unless the reporter explicitly implements
multiplexing. Multi-threaded work should aggregate counters and report them
through the operation-scoped Progress.
use Duration;
use ;
let schema = new;
let reporter = from_writer;
let mut progress = new;
progress.report_started;
progress.report_running;
progress.report_finished;
report_running_if_due only invokes the builder closure when the configured
interval has elapsed. This keeps hot paths cheap for positive intervals.
Background Reporter Thread
Use Progress::spawn_running_reporter when worker threads update domain state
and a coordinating thread should emit periodic running events. Workers update
shared state, then call RunningProgressPointHandle::report() to wake the
background reporter thread when the interval is Duration::ZERO.
RunningProgressGuard owns that background reporter thread, and
RunningProgressPointHandle is the cloneable worker-side wakeup handle.
The example below uses qubit-atomic's ArcAtomic; add
qubit-atomic = "0.13" if you copy this pattern into your own crate.
use ;
use ArcAtomic;
use ;
let reporter = NoOpProgressReporter;
let completed = new;
let progress = new;
scope;
Reporter Implementations
Reporters receive immutable ProgressEvent values through ProgressReporter:
;
Built-in reporters:
| Reporter | Purpose |
|---|---|
NoOpProgressReporter |
ignores events |
MetricSnapshotProgressReporter |
sends structured ProgressMetricSnapshot objects to a consumer |
FormattedProgressReporter |
formats each metric snapshot and sends strings to a consumer |
HumanReadableProgressReporter |
sends human-readable metric snapshot strings to a consumer |
JsonProgressReporter |
sends JSON metric snapshot strings to a consumer |
WriterProgressReporter |
writes human-readable metric snapshot lines to any Write sink |
StdoutProgressReporter |
writes to stdout |
StderrProgressReporter |
writes to stderr |
LoggerProgressReporter |
emits through the log crate |
JsonWriterProgressReporter |
writes JSON metric snapshot lines to any Write sink |
JsonStdoutProgressReporter |
writes JSON metric snapshots to stdout |
JsonStderrProgressReporter |
writes JSON metric snapshots to stderr |
JsonLoggerProgressReporter |
emits JSON metric snapshots through the log crate |
A reporter can call event.metric_snapshots() to turn each counter into a
ProgressMetricSnapshot containing the complete metric object, phase, optional
stage, flattened counter values, and elapsed time.
JSON Serialization
Progress events are serde-serializable. elapsed uses the duration_with_unit
adapter from qubit-serde, so JSON is compact and agent-friendly.
use Duration;
use ;
let schema = new;
let event = builder
.running
.counter
.elapsed
.build;
let json = to_string.expect;
assert_eq!;
Crate Boundary
qubit-progress provides progress data models, operation-scoped lifecycle
helpers, and reporter abstractions. It intentionally does not provide terminal
UI widgets, async runtime integration, task scheduling, tracing infrastructure,
or long-term metrics storage.
Runtime Dependencies
This crate depends on:
serdefor serializable progress models;serde_jsonfor built-in JSON metric snapshot formatting;logforLoggerProgressReporter;qubit-functionfor consumer adapters used by formatted reporters;qubit-serdefor compactDurationserialization.
It does not require an async runtime.
Testing & Code Coverage
This project maintains test coverage for progress models, metric snapshots, event builders, reporting cadence, background reporting, text and JSON reporter implementations, and JSON serialization.
Running Tests
# Run all tests
# Run with coverage report
# Generate text format report
# Run CI checks (format, clippy, test, coverage, audit)
License
Copyright (c) 2026. Haixing Hu.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
See LICENSE for the full license text.
Contributing
Contributions are welcome. Please feel free to submit a Pull Request.
Development Guidelines
- Follow the Rust API guidelines.
- Keep progress reporting concerns in
qubit-progress. - Keep events immutable and self-describing.
- Keep reporter implementations small and explicit.
- Maintain comprehensive test coverage.
- Document public APIs with examples when they clarify behavior.
- Ensure
./ci-check.shpasses before submitting a PR.
Author
Haixing Hu
Related Projects
- qubit-serde: serde helpers used by Qubit Rust crates.
- More Rust libraries from Qubit are published under the qubit-ltd organization on GitHub.
Repository: https://github.com/qubit-ltd/rs-progress