diagprint
diagprint is a Rust diagnostics lifecycle framework.
Define diagnostics once, then carry them safely through terminal output, compiler tooling, editors, CI, code scanning, guarded remediation, verification, telemetry, and privacy-aware external reporting.
diagprint turns failures into actionable, structured diagnostics with:
- rich terminal rendering;
- primary and secondary source labels;
- virtual and in-memory sources;
- immutable source snapshots;
- per-source revision tracking;
- stale-source detection;
- captured revision-aware diagnostics;
- JSON, Markdown, plain-text, GitHub Actions, and SARIF output;
- ecosystem interoperability;
- rustc and Cargo diagnostic ingestion;
- guarded remediation;
- transactional multi-file fixes;
- post-fix verification;
- version-aware documentation.
The core rule is:
Diagnostics may explain and propose. Mutation must be explicit, structured, validated, and reject uncertainty.
Installation
[]
= "0.6"
diagprint v0.6.0 uses Rust 2024 and supports Rust 1.85 and newer.
Optional Features
All optional features are disabled by default.
[]
= {
version = "0.6",
= [
"compression",
"cybercore",
"terminal-docs",
"anyhow",
"tracing",
"miette",
"codespan-reporting",
"ariadne",
"annotate-snippets",
]
}
| Feature | Purpose |
|---|---|
compression |
gzip and Zstandard report compression |
cybercore |
Cybercore theme integration |
terminal-docs |
terminal documentation retrieval and highlighting |
anyhow |
anyhow diagnostic integration |
tracing |
tracing subscriber integration |
miette |
miette interoperability |
codespan-reporting |
codespan-reporting interoperability |
ariadne |
Ariadne structured bridge |
annotate-snippets |
annotate-snippets structured bridge |
Quick Start
use ;
Source Diagnostics
Diagnostics can point directly at source locations.
let diagnostic = reporter
.error
.code
.label
.secondary_label;
Primary and secondary labels remain structurally distinct through rendering and interop.
The terminal renderer displays them differently so related context does not look like an additional primary failure.
Virtual and In-Memory Sources
Source text does not need to exist on disk.
let reporter = builder
.application
.source
.build?;
let diagnostic = reporter
.error
.label;
reporter.emit?;
SourceCache stores virtual or generated source text:
use SourceCache;
let cache = new;
cache.insert;
Source names are matched exactly.
When source-aware terminal rendering is used, cached source takes precedence over filesystem fallback.
Cloned SourceCache handles share the same underlying source state.
Immutable Source Snapshots
Mutable editor buffers can change after a diagnostic is created.
SourceSnapshot freezes the source view at a point in time while source text
itself remains shared through Arc.
let cache = reporter.source_cache;
let snapshot = cache.snapshot;
let diagnostic = reporter
.error
.label
.bind_source_revisions;
Later mutations to the live cache do not change the snapshot.
Source Revisions
Every cached source tracks a SourceRevision.
let revision = cache.insert_revisioned;
println!;
Revisions are tracked independently per source.
Every insertion advances the revision, including replacement with identical text.
Revision history survives removal and clearing so an old revision cannot be silently reused after a source is recreated.
Revision-Bound Diagnostics
A diagnostic can bind its source locations to the source revision it was created from.
let snapshot = reporter.source_snapshot;
let diagnostic = reporter
.error
.label
.bind_source_revisions;
If the live source later changes, a revision-aware terminal render fails closed.
Instead of underlining unrelated newer text, it reports the mismatch:
! stale source: r1 != r2
No misleading newer source excerpt is shown.
Revision-unbound diagnostics retain the existing source behavior.
Captured Diagnostics
CapturedDiagnostic pairs a diagnostic with the immutable source snapshot that
belongs to it.
let captured = reporter.capture;
The live buffer may then continue changing:
reporter.register_source;
assert!;
The captured diagnostic still renders against its original immutable source:
reporter.emit_captured?;
Source text stays outside Diagnostic serialization.
Source Providers
Integrations which own in-memory source text can expose it through
SourceProvider.
A reporter can register those sources:
reporter.register_sources;
Or a builder can import them:
let reporter = builder
.sources_from
.build?;
The Ariadne and annotate-snippets bridges implement this handoff.
GitHub Actions Annotations
diagprint can emit native GitHub Actions workflow-command annotations.
let diagnostic = reporter
.error
.code
.label
.secondary_label;
reporter.emit_github_actions?;
Severity mapping:
| diagprint | GitHub Actions |
|---|---|
| Trace | notice |
| Debug | notice |
| Info | notice |
| Warning | warning |
| Error | error |
| Fatal | error |
Primary labels retain the diagnostic severity.
Secondary labels are emitted as notices so related source locations remain visible without appearing as additional failures.
Workflow command data and properties are escaped before output.
SARIF 2.1.0
SarifRenderer produces SARIF for GitHub Code Scanning and other SARIF 2.1.0
consumers.
use SarifRenderer;
let first = reporter
.error
.code
.label
.secondary_label;
let second = reporter
.warning
.code
.label;
SarifRenderer.write_many?;
SARIF output includes:
- SARIF 2.1.0 metadata;
- deterministic rule IDs;
- deterministic rule indices;
- severity mapping;
- primary source locations;
- related source locations;
- exclusive SARIF end-column ranges;
- notes;
- help;
- cause information;
- diagprint source revision metadata.
No synthetic fingerprints are invented.
write_many() writes one complete SARIF document rather than appending
independent JSON documents.
Built-In Output Formats
The same diagnostic data can be rendered as:
- terminal output;
- plain text;
- JSON;
- Markdown;
- GitHub Actions annotations;
- SARIF 2.1.0.
use ;
Diagnostic construction stays independent from presentation.
Generic Diagnostic Interop
diagprint provides a dependency-free interoperability protocol for structured
diagnostics.
The generic protocol can preserve:
- severity;
- diagnostic codes;
- messages;
- help;
- notes;
- source labels;
- primary and secondary label roles;
- causes;
- documentation links;
- related diagnostics.
Generic interoperability deliberately does not grant remediation trust.
An integration that wants automatic edits must establish remediation trust separately.
anyhow
Enable:
= ["anyhow"]
The anyhow adapter can convert error context into structured diagprint diagnostics while preserving the error chain.
tracing
Enable:
= ["tracing"]
The tracing integration connects structured tracing events with diagprint reporting.
miette
Enable:
= ["miette"]
The miette integration maps compatible diagnostic metadata into diagprint's structured interoperability model.
codespan-reporting
Enable:
= ["codespan-reporting"]
Codespan diagnostics are routed through the generic interoperability layer.
Ariadne
Enable:
= ["ariadne"]
AriadneBridge captures structured source and label metadata once and can
produce both:
- a real Ariadne report;
- a diagprint interoperability diagnostic.
The bridge does not parse rendered Ariadne terminal text and does not rely on private Ariadne internals.
Ariadne source spans are resolved into one-based source locations for diagprint.
annotate-snippets
Enable:
= ["annotate-snippets"]
The annotate-snippets bridge:
- preserves primary and context labels;
- validates byte ranges;
- rejects invalid UTF-8 boundaries;
- converts byte spans into one-based line and column locations;
- exposes its in-memory sources through
SourceProvider.
Rustc Diagnostics
diagprint can ingest structured rustc JSON diagnostics.
The compiler integration can preserve information including:
- severity;
- error codes;
- source spans;
- child diagnostics;
- structured suggestions;
- applicability;
- compiler source edits;
- rendered compiler context when available.
Compiler edits require explicit trusted source-root hydration before they can participate in remediation.
Cargo Intelligence
Cargo ingestion can track information including:
- workspace packages;
- exact package versions;
- workspace membership;
- targets;
- resolved dependencies;
- renamed dependencies;
- compiler artifacts;
- build-script results;
- build completion;
- build summaries.
Unknown Cargo messages are preserved for forward compatibility.
Diagnostic Suggestions
Diagnostics can carry structured suggestions.
use ;
let suggestion =
new
.applicability
.edit;
Applicability levels are:
Only guarded, machine-applicable structured edits are eligible for automatic application.
Fixer
Validate without writing:
use Fixer;
let check =
new
.check?;
Apply validated fixes:
let report =
new
.backups
.apply?;
Interactive mode:
new
.backups
.apply_interactive?;
Before mutation, diagprint validates:
- applicability;
- expected source contents;
- edit ranges;
- UTF-8 boundaries;
- overlapping edits;
- duplicate insertion positions;
- filesystem state.
Stale edits are rejected instead of guessed.
FixPlan
FixPlan supports transaction-wide multi-file remediation.
The remediation flow:
- validates all affected files;
- prepares all resulting contents;
- creates recovery state;
- performs transaction writes;
- rolls back observed failures;
- optionally performs post-fix verification;
- rolls back when verification fails.
Portable crash-atomic multi-file writes are not claimed.
Post-Fix Verification
Fix plans can declare structured verification requirements after application.
Verification remains declarative.
diagprint does not automatically execute arbitrary shell commands as part of
fix application or verification.
Suggested Commands
Suggestions may contain advisory commands:
use SuggestedCommand;
let command =
new
.explanation;
Suggested commands are never executed automatically.
Documentation Intelligence
DocumentationResolver supports version-aware documentation resolution using
Cargo metadata and lockfiles.
Supported documentation targets include:
- Rust error documentation;
- Cargo Book pages;
- docs.rs package documentation;
- custom documentation links.
Ambiguous package versions fail closed instead of guessing a docs.rs version.
Terminal Documentation
Enable:
= ["terminal-docs"]
Then:
use ;
let link =
rust_error;
new
.width
.open_and_print?;
The terminal documentation viewer:
- accepts HTTP and HTTPS documentation URLs;
- limits remote document size;
- sanitizes terminal control characters;
- converts HTML to terminal-readable text;
- extracts code examples;
- syntax-highlights code.
The current documentation viewer uses blocking I/O.
Themes
Terminal presentation is customizable.
use ;
let theme = Theme ;
Styles support:
- standard ANSI colors;
- ANSI-256 colors;
- RGB foregrounds and backgrounds;
- hex colors;
- bold;
- dim;
- italic;
- underline.
color(false) remains authoritative and disables ANSI styling regardless of
theme configuration.
Cybercore Integration
Enable:
= ["cybercore"]
Use the active Cybercore theme:
use Theme;
let theme =
cybercore;
Named Cybercore themes are also supported.
The integration consumes Cybercore's semantic palette rather than duplicating theme values inside diagprint.
Rotation
File reports support:
- size-based rotation;
- hourly rotation;
- daily rotation;
- retention cleanup.
use ;
let policy = RotationPolicy ;
Compression
Enable:
= ["compression"]
Supported compression formats:
use Compression;
// Compression::Gzip
// Compression::Zstd
Safety Model
diagprint deliberately separates diagnostic presentation from mutation.
Rendering never modifies source files.
Automatic remediation is restricted to structured edits that:
- are marked
MachineApplicable; - still match expected source contents;
- use valid UTF-8 boundaries;
- do not overlap;
- satisfy declared preconditions.
Additional safeguards include:
- stale-edit rejection;
- guarded insertion requirements;
- transaction-wide validation;
- recovery state before writes;
- rollback on observed write failures;
- optional post-fix verification;
- verification rollback;
- trusted-root requirements for hydrated compiler edits;
- fail-closed documentation version resolution.
Suggested shell commands are never automatically executed.
Revision-aware source rendering also fails closed when source identity no longer matches the diagnostic.
Minimum Supported Rust Version
The minimum supported Rust version is:
Rust 1.85
CI performs an MSRV-aware fresh dependency resolution and checks all targets and all features on Rust 1.85.
Development
Default gate:
Full feature gate:
MSRV gate:
CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=fallback \
Examples
Core examples:
Revision-aware source examples:
CI output examples:
Interop examples:
Other optional examples:
v0.6
Revision-Aware Diagnostics and CI Output
v0.6 adds:
- distinct primary and secondary label rendering;
- virtual source caching;
- generic source-provider handoff;
- immutable source snapshots;
- per-source revision tracking;
- revision-bound source locations;
- stale-source detection;
- fail-closed stale-source terminal rendering;
- captured diagnostics;
- Ariadne structured interoperability;
- annotate-snippets structured interoperability;
- GitHub Actions annotations;
- SARIF 2.1.0 rendering;
- hardened Rust 1.85 validation across all targets and features.
v0.5
Interoperability and Transactional Remediation
v0.5 added:
- Rust 2024;
- Rust 1.85 MSRV;
- anyhow integration;
- typed-error metadata;
- tracing integration;
- rustc/Cargo structured ingestion;
- version-aware documentation;
- FixPlan;
- transaction-wide multi-file remediation;
- post-fix verification;
- Cargo intelligence;
- miette interoperability;
- codespan-reporting interoperability;
- generic dependency-free diagnostic interoperability.
Roadmap
Potential future work includes:
- asynchronous and nonblocking report output;
- asynchronous terminal documentation retrieval;
- richer structured diff presentation;
- additional structured fix sources.
Repository
https://github.com/darkstardevx/diagprint
License
Licensed under either:
- Apache License, Version 2.0
- MIT License
at your option.